mirror of
https://github.com/libretro/beetle-psx-libretro.git
synced 2024-11-23 08:49:47 +00:00
Cleanups for RetroFen
This commit is contained in:
parent
32a02748fe
commit
e515b9ab96
7
Makefile
7
Makefile
@ -66,19 +66,15 @@ PSX_SOURCES := $(PSX_DIR)/psx.cpp \
|
||||
|
||||
MEDNAFEN_SOURCES := $(MEDNAFEN_DIR)/cdrom/cdromif.cpp \
|
||||
$(MEDNAFEN_DIR)/mednafen.cpp \
|
||||
$(MEDNAFEN_DIR)/PSFLoader.cpp \
|
||||
$(MEDNAFEN_DIR)/error.cpp \
|
||||
$(MEDNAFEN_DIR)/math_ops.cpp \
|
||||
$(MEDNAFEN_DIR)/settings.cpp \
|
||||
$(MEDNAFEN_DIR)/general.cpp \
|
||||
$(MEDNAFEN_DIR)/player.cpp \
|
||||
$(MEDNAFEN_DIR)/cdplay.cpp \
|
||||
$(MEDNAFEN_DIR)/FileWrapper.cpp \
|
||||
$(MEDNAFEN_DIR)/FileStream.cpp \
|
||||
$(MEDNAFEN_DIR)/MemoryStream.cpp \
|
||||
$(MEDNAFEN_DIR)/Stream.cpp \
|
||||
$(MEDNAFEN_DIR)/state.cpp \
|
||||
$(MEDNAFEN_DIR)/tests.cpp \
|
||||
$(MEDNAFEN_DIR)/endian.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/CDAccess.cpp \
|
||||
$(MEDNAFEN_DIR)/cdrom/CDAccess_Image.cpp \
|
||||
@ -97,7 +93,6 @@ MEDNAFEN_SOURCES := $(MEDNAFEN_DIR)/cdrom/cdromif.cpp \
|
||||
$(MEDNAFEN_DIR)/video/video.cpp \
|
||||
$(MEDNAFEN_DIR)/video/text.cpp \
|
||||
$(MEDNAFEN_DIR)/video/font-data.cpp \
|
||||
$(MEDNAFEN_DIR)/video/tblur.cpp \
|
||||
$(MEDNAFEN_DIR)/video/Deinterlacer.cpp \
|
||||
$(MEDNAFEN_DIR)/video/surface.cpp \
|
||||
$(MEDNAFEN_DIR)/video/resize.cpp \
|
||||
@ -159,7 +154,7 @@ WARNINGS := -Wall \
|
||||
|
||||
FLAGS += $(ENDIANNESS_DEFINES) -DSIZEOF_DOUBLE=8 $(WARNINGS) \
|
||||
-DMEDNAFEN_VERSION=\"0.9.25\" -DPACKAGE=\"mednafen\" -DMEDNAFEN_VERSION_NUMERIC=925 -DPSS_STYLE=1 -DMPC_FIXED_POINT -DARCH_X86 \
|
||||
-DWANT_PSX_EMU -DSTDC_HEADERS -D__STDC_LIMIT_MACROS
|
||||
-DWANT_PSX_EMU -DSTDC_HEADERS -D__STDC_LIMIT_MACROS -D__LIBRETRO__
|
||||
|
||||
CXXFLAGS += $(FLAGS)
|
||||
CFLAGS += $(FLAGS) -std=gnu99
|
||||
|
@ -1,335 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
TODO:
|
||||
Time string parsing convenience functions.
|
||||
|
||||
Character set autodetect heuristics and conversion for when the "utf8" tag is missing.
|
||||
*/
|
||||
#include "mednafen.h"
|
||||
#include "PSFLoader.h"
|
||||
#include "mednafen-endian.h"
|
||||
#include "general.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <trio/trio.h>
|
||||
#include <ctype.h>
|
||||
//#include <iconv.h>
|
||||
|
||||
|
||||
PSFTags::PSFTags()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PSFTags::~PSFTags()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PSFTags::AddTag(char *tag_line)
|
||||
{
|
||||
char *eq;
|
||||
|
||||
// Transform 0x01-0x1F -> 0x20
|
||||
for(unsigned int i = 0; i < strlen(tag_line); i++)
|
||||
if((unsigned char)tag_line[i] < 0x20)
|
||||
tag_line[i] = 0x20;
|
||||
|
||||
eq = strchr(tag_line, '=');
|
||||
|
||||
if(eq)
|
||||
{
|
||||
*eq = 0;
|
||||
|
||||
MDFN_trim(tag_line);
|
||||
MDFN_trim(eq + 1);
|
||||
|
||||
for(unsigned int i = 0; i < strlen(tag_line); i++)
|
||||
tag_line[i] = tolower(tag_line[i]);
|
||||
|
||||
if(TagExists(tag_line))
|
||||
tags[tag_line] = tags[std::string(tag_line)] + std::string(1, '\n') + std::string(eq + 1);
|
||||
else
|
||||
tags[tag_line] = std::string(eq + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char *DetectCharset(const uint8 *data, const uint32 data_size)
|
||||
{
|
||||
static const char *TestCharsets[] = { "UTF-8", /*"SJIS",*/ "WINDOWS-1252" };
|
||||
|
||||
for(unsigned int i = 0; i < sizeof(TestCharsets) / sizeof(TestCharsets[0]); i++)
|
||||
{
|
||||
iconv_t cd;
|
||||
|
||||
cd = iconv_open("UTF-32", TestCharsets[i]);
|
||||
if(cd != (iconv_t)-1)
|
||||
{
|
||||
size_t in_len = data_size;
|
||||
size_t out_len = data_size * 4 + 4;
|
||||
char *in_ptr = (char *)data;
|
||||
char *const out_ptr_mem = new char[out_len];
|
||||
char *out_ptr = out_ptr_mem;
|
||||
|
||||
if(iconv(cd, (ICONV_CONST char **)&in_ptr, &in_len, &out_ptr, &out_len) != (size_t)-1)
|
||||
{
|
||||
delete[] out_ptr_mem;
|
||||
return(TestCharsets[i]);
|
||||
}
|
||||
delete[] out_ptr_mem;
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
void PSFTags::LoadTags(const uint8 *data_in, uint32 size)
|
||||
{
|
||||
std::vector<char> tags_heap;
|
||||
char *data;
|
||||
char *spos;
|
||||
//const char *detected_charset = DetectCharset(data_in, size);
|
||||
|
||||
tags_heap.resize(size + 1);
|
||||
tags_heap[size] = 0;
|
||||
|
||||
memcpy(&tags_heap[0], data_in, size);
|
||||
|
||||
data = &tags_heap[0];
|
||||
spos = data;
|
||||
|
||||
while(size)
|
||||
{
|
||||
if(*data == 0x0A || *data == 0x00)
|
||||
{
|
||||
*data = 0;
|
||||
|
||||
if(data - spos)
|
||||
{
|
||||
if(*(data - 1) == 0xD) // handle \r
|
||||
*(data - 1) = 0;
|
||||
|
||||
AddTag(spos);
|
||||
}
|
||||
|
||||
spos = data + 1; // Skip \n for next tag
|
||||
}
|
||||
|
||||
size--;
|
||||
data++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int64 PSFTags::GetTagI(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
if(it != tags.end())
|
||||
{
|
||||
long long ret = 0;
|
||||
std::string &tmp = tags[name];
|
||||
|
||||
trio_sscanf(tmp.c_str(), "%lld", &ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
return(0); // INT64_MIN
|
||||
}
|
||||
|
||||
bool PSFTags::TagExists(const char *name)
|
||||
{
|
||||
if(tags.find(name) != tags.end())
|
||||
return(true);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
std::string PSFTags::GetTag(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
|
||||
if(it != tags.end())
|
||||
return(it->second);
|
||||
|
||||
return("");
|
||||
}
|
||||
|
||||
void PSFTags::EraseTag(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
if(it != tags.end())
|
||||
tags.erase(it);
|
||||
}
|
||||
|
||||
PSFLoader::PSFLoader()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PSFLoader::~PSFLoader()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool PSFLoader::TestMagic(uint8 version, MDFNFILE *fp)
|
||||
{
|
||||
if(fp->size < (3 + 1 + 4 + 4 + 4))
|
||||
return(false);
|
||||
|
||||
if(memcmp(fp->data, "PSF", 3))
|
||||
return(false);
|
||||
|
||||
if(fp->data[3] != version)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
PSFTags PSFLoader::LoadInternal(uint8 version, uint32 max_exe_size, MDFNFILE *fp, uint32 level, bool force_ignore_pcsp)
|
||||
{
|
||||
uint32 reserved_size, compressed_size, compressed_crc32;
|
||||
bool _lib_present = false;
|
||||
PSFTags tags;
|
||||
|
||||
std::vector<uint8> decompress_buffer;
|
||||
uLongf decompress_len;
|
||||
|
||||
if(!TestMagic(version, fp))
|
||||
throw(MDFN_Error(0, _("Not a PSF(version=0x%02x) file!"), version));
|
||||
|
||||
reserved_size = MDFN_de32lsb(fp->data + 4);
|
||||
compressed_size = MDFN_de32lsb(fp->data + 8);
|
||||
compressed_crc32 = MDFN_de32lsb(fp->data + 12);
|
||||
|
||||
if(fp->size < (16 + reserved_size + compressed_size))
|
||||
throw(MDFN_Error(0, _("PSF is missing at least %u bytes of data!"), 16 + reserved_size + compressed_size - fp->size));
|
||||
|
||||
if(crc32(0, fp->data + 16 + reserved_size, compressed_size) != compressed_crc32)
|
||||
throw(MDFN_Error(0, _("PSF compressed CRC32 mismatch(data is corrupt)!")));
|
||||
|
||||
|
||||
{
|
||||
const uint8 *tag_section = fp->data + 16 + reserved_size + compressed_size;
|
||||
uint32 tag_section_size = fp->size - 16 - reserved_size - compressed_size;
|
||||
|
||||
if(tag_section_size > 5 && !memcmp(tag_section, "[TAG]", 5))
|
||||
tags.LoadTags(tag_section + 5, tag_section_size - 5);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle minipsf simple _lib
|
||||
//
|
||||
|
||||
if(level < 15)
|
||||
{
|
||||
if(tags.TagExists("_lib"))
|
||||
{
|
||||
std::string tp = tags.GetTag("_lib");
|
||||
|
||||
if(!MDFN_IsFIROPSafe(tp))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting."), tp.c_str()));
|
||||
}
|
||||
|
||||
MDFNFILE subfile(MDFN_MakeFName(MDFNMKF_AUX, 0, tp.c_str()).c_str(), NULL, NULL);
|
||||
|
||||
LoadInternal(version, max_exe_size, &subfile, level + 1);
|
||||
|
||||
_lib_present = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
decompress_buffer.resize(max_exe_size);
|
||||
decompress_len = max_exe_size;
|
||||
switch( uncompress((Bytef *)&decompress_buffer[0], &decompress_len, (const Bytef *)(fp->data + 16 + reserved_size), compressed_size) )
|
||||
{
|
||||
default:
|
||||
throw(MDFN_Error(0, "zlib unknown error"));
|
||||
|
||||
case Z_OK: break;
|
||||
|
||||
case Z_MEM_ERROR:
|
||||
throw(MDFN_Error(0, "zlib Z_MEM_ERROR"));
|
||||
|
||||
case Z_BUF_ERROR:
|
||||
throw(MDFN_Error(0, _("PSF decompressed size exceeds maximum allowed!")));
|
||||
|
||||
case Z_DATA_ERROR:
|
||||
throw(MDFN_Error(0, _("PSF compressed data is bad.")));
|
||||
}
|
||||
|
||||
HandleReserved(fp->data + 16, reserved_size);
|
||||
HandleEXE(&decompress_buffer[0], decompress_len, force_ignore_pcsp | _lib_present);
|
||||
decompress_buffer.resize(0);
|
||||
|
||||
//
|
||||
// handle libN
|
||||
//
|
||||
if(level < 15)
|
||||
{
|
||||
for(unsigned int n = 2; n <= INT_MAX; n++)
|
||||
{
|
||||
char tmpbuf[32];
|
||||
|
||||
trio_snprintf(tmpbuf, 32, "_lib%d", (int)n);
|
||||
|
||||
if(tags.TagExists(tmpbuf))
|
||||
{
|
||||
MDFNFILE subfile(MDFN_MakeFName(MDFNMKF_AUX, 0, tags.GetTag(tmpbuf).c_str()).c_str(), NULL, NULL);
|
||||
|
||||
LoadInternal(version, max_exe_size, &subfile, level + 1, true);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(tags);
|
||||
}
|
||||
|
||||
PSFTags PSFLoader::Load(uint8 version, uint32 max_exe_size, MDFNFILE *fp)
|
||||
{
|
||||
return(LoadInternal(version, max_exe_size, fp, 0, false));
|
||||
}
|
||||
|
||||
void PSFLoader::HandleReserved(const uint8 *data, uint32 len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PSFLoader::HandleEXE(const uint8 *data, uint32 len, bool ignore_pcsp)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
#ifndef __MDFN_PSFLOADER_H
|
||||
#define __MDFN_PSFLOADER_H
|
||||
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class PSFTags
|
||||
{
|
||||
public:
|
||||
|
||||
PSFTags();
|
||||
~PSFTags();
|
||||
|
||||
int64 GetTagI(const char *name);
|
||||
std::string GetTag(const char *name);
|
||||
bool TagExists(const char *name);
|
||||
|
||||
void LoadTags(const uint8 *data, uint32 size);
|
||||
void EraseTag(const char *name);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void AddTag(char *tag_line);
|
||||
std::map<std::string, std::string> tags;
|
||||
};
|
||||
|
||||
class PSFLoader
|
||||
{
|
||||
public:
|
||||
PSFLoader();
|
||||
~PSFLoader();
|
||||
|
||||
static bool TestMagic(uint8 version, MDFNFILE *fp);
|
||||
|
||||
PSFTags Load(uint8 version, uint32 max_exe_size, MDFNFILE *fp);
|
||||
|
||||
virtual void HandleReserved(const uint8 *data, uint32 len);
|
||||
virtual void HandleEXE(const uint8 *data, uint32 len, bool ignore_pcsp = false);
|
||||
|
||||
private:
|
||||
|
||||
PSFTags LoadInternal(uint8 version, uint32 max_exe_size, MDFNFILE *fp, uint32 level, bool force_ignore_pcsp = false);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,589 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
//#include <mednafen/mednafen.h>
|
||||
#include "mednafen.h"
|
||||
#include "cdrom/cdromif.h"
|
||||
#include "netplay.h"
|
||||
#include <blip/Blip_Buffer.h>
|
||||
#include <trio/trio.h>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
#include <mednafen/resampler/resampler.h>
|
||||
|
||||
namespace MDFN_IEN_CDPLAY
|
||||
{
|
||||
|
||||
static std::vector<double> sqrt_lut; //[65536];
|
||||
static std::vector<double> sin_lut; //[65536];
|
||||
static SpeexResamplerState *resampler = NULL;
|
||||
|
||||
static uint8 *controller_ptr;
|
||||
static uint8 last_controller;
|
||||
|
||||
enum
|
||||
{
|
||||
PLAYMODE_PAUSE = -1,
|
||||
PLAYMODE_STOP = 0,
|
||||
PLAYMODE_PLAY = 1,
|
||||
PLAYMODE_SCAN_FORWARD = 2,
|
||||
PLAYMODE_SCAN_REVERSE = 3,
|
||||
};
|
||||
|
||||
static int PlayMode;
|
||||
static int32 PlaySector;
|
||||
static int16 CDDABuffer[588 * 2];
|
||||
|
||||
static int16 ResampBuffer[588 * 2][2]; // Resampler input buffer, * 2 for resampler leftovers
|
||||
static uint32 ResampBufferPos;
|
||||
static uint32 PrevRate;
|
||||
|
||||
static std::vector<CDIF *> *cdifs;
|
||||
|
||||
static int32 CurrentATLI;
|
||||
|
||||
struct AudioTrackInfo
|
||||
{
|
||||
inline AudioTrackInfo(unsigned disc_, int32 track_, int32 lba_, int32 final_lba_)
|
||||
{
|
||||
disc = disc_;
|
||||
track = track_;
|
||||
lba = lba_;
|
||||
final_lba = final_lba_;
|
||||
}
|
||||
|
||||
unsigned disc;
|
||||
int32 track;
|
||||
int32 lba;
|
||||
int32 final_lba; // Inclusive.
|
||||
};
|
||||
|
||||
static std::vector<AudioTrackInfo> AudioTrackList;
|
||||
|
||||
static void InitLUT(void);
|
||||
|
||||
static int LoadCD(std::vector<CDIF *> *CDInterfaces)
|
||||
{
|
||||
cdifs = CDInterfaces;
|
||||
|
||||
AudioTrackList.clear();
|
||||
|
||||
for(unsigned disc = 0; disc < cdifs->size(); disc++)
|
||||
{
|
||||
TOC toc;
|
||||
|
||||
(*cdifs)[disc]->ReadTOC(&toc);
|
||||
|
||||
for(int32 track = toc.first_track; track <= toc.last_track; track++)
|
||||
{
|
||||
if(!(toc.tracks[track].control & 0x4))
|
||||
{
|
||||
AudioTrackList.push_back(AudioTrackInfo(disc, track, toc.tracks[track].lba, toc.tracks[track + 1].lba - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!AudioTrackList.size())
|
||||
{
|
||||
puts("Audio track doesn't exist");
|
||||
return(0);
|
||||
}
|
||||
|
||||
CurrentATLI = 0;
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
PlayMode = PLAYMODE_PLAY; //STOP;
|
||||
|
||||
{
|
||||
int err;
|
||||
resampler = speex_resampler_init(2, 44100, (int)48000, 5, &err);
|
||||
PrevRate = 48000;
|
||||
}
|
||||
//resampler.buffer_size(588 * 2 + 100);
|
||||
//resampler.time_ratio((double)44100 / 48000, 0.9965);
|
||||
ResampBufferPos = 0;
|
||||
|
||||
InitLUT();
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static bool TestMagicCD(std::vector<CDIF *> *CDInterfaces)
|
||||
{
|
||||
CDUtility::TOC magic_toc;
|
||||
|
||||
for(unsigned i = 0; i < CDInterfaces->size(); i++)
|
||||
{
|
||||
(*CDInterfaces)[i]->ReadTOC(&magic_toc);
|
||||
|
||||
// If any audio track is found, return true.
|
||||
for(int32 track = magic_toc.first_track; track <= magic_toc.last_track; track++)
|
||||
if(!(magic_toc.tracks[track].control & 0x4))
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
static void CloseGame(void)
|
||||
{
|
||||
if(resampler)
|
||||
{
|
||||
speex_resampler_destroy(resampler);
|
||||
resampler = NULL;
|
||||
}
|
||||
sin_lut.resize(0);
|
||||
sqrt_lut.resize(0);
|
||||
}
|
||||
|
||||
static uint8 SubQBuf[3][0xC];
|
||||
|
||||
static void GenSubQFromSubPW(uint8 *SubPWBuf)
|
||||
{
|
||||
uint8 sq[0xC];
|
||||
|
||||
memset(sq, 0, 0xC);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
sq[i >> 3] |= ((SubPWBuf[i] & 0x40) >> 6) << (7 - (i & 7));
|
||||
|
||||
if(!subq_check_checksum(sq))
|
||||
puts("SubQ checksum error!");
|
||||
else
|
||||
{
|
||||
uint8 adr = sq[0] & 0xF;
|
||||
|
||||
if(adr <= 0x3)
|
||||
memcpy(SubQBuf[adr], sq, 0xC);
|
||||
}
|
||||
}
|
||||
static const int lobes = 2;
|
||||
static const int oversample_shift = 7; //1; //7;
|
||||
static const int oversample = 1 << oversample_shift;
|
||||
static const int oversample_mo = oversample - 1;
|
||||
|
||||
static void InitLUT(void)
|
||||
{
|
||||
sqrt_lut.resize(65536);
|
||||
sin_lut.resize(65536);
|
||||
|
||||
for(int i = 0; i < 65536; i++)
|
||||
sqrt_lut[i] = sqrt((double)i / 65536);
|
||||
|
||||
for(int i = 0; i < 65536; i++)
|
||||
sin_lut[i] = sin((double)i * M_PI * 2 / 65536);
|
||||
}
|
||||
|
||||
static void Emulate(EmulateSpecStruct *espec)
|
||||
{
|
||||
uint8 sector_buffer[2352 + 96];
|
||||
uint8 new_controller = *controller_ptr;
|
||||
|
||||
espec->MasterCycles = 588;
|
||||
|
||||
//printf("%d %d\n", toc.tracks[100].lba, AudioTrackList[AudioTrackList.size() - 1] + 1);
|
||||
|
||||
if(PlaySector < AudioTrackList[CurrentATLI].lba) // Reverse-scanning handling.
|
||||
{
|
||||
if(CurrentATLI > 0)
|
||||
{
|
||||
CurrentATLI--;
|
||||
PlaySector = AudioTrackList[CurrentATLI].final_lba;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentATLI = 0;
|
||||
PlayMode = PLAYMODE_STOP;
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
}
|
||||
else if(PlaySector > AudioTrackList[CurrentATLI].final_lba)
|
||||
{
|
||||
if((CurrentATLI + 1) < AudioTrackList.size())
|
||||
CurrentATLI++;
|
||||
else
|
||||
{
|
||||
CurrentATLI = 0;
|
||||
PlayMode = PLAYMODE_STOP;
|
||||
}
|
||||
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
|
||||
if(PlayMode == PLAYMODE_STOP || PlayMode == PLAYMODE_PAUSE)
|
||||
{
|
||||
//memset(CDDABuffer, 0, sizeof(CDDABuffer));
|
||||
for(int i = 0; i < 588; i++)
|
||||
{
|
||||
ResampBuffer[ResampBufferPos][0] = 0;
|
||||
ResampBuffer[ResampBufferPos][1] = 0;
|
||||
ResampBufferPos++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(*cdifs)[AudioTrackList[CurrentATLI].disc]->ReadRawSector(sector_buffer, PlaySector);
|
||||
GenSubQFromSubPW(sector_buffer + 2352);
|
||||
|
||||
for(int i = 0; i < 588 * 2; i++)
|
||||
{
|
||||
CDDABuffer[i] = MDFN_de16lsb(§or_buffer[i * sizeof(int16)]);
|
||||
|
||||
ResampBuffer[ResampBufferPos + (i >> 1)][i & 1] = CDDABuffer[i] / 2;
|
||||
}
|
||||
ResampBufferPos += 588;
|
||||
}
|
||||
|
||||
if(espec->SoundBuf)
|
||||
{
|
||||
if((int)espec->SoundRate == 44100)
|
||||
{
|
||||
memcpy(espec->SoundBuf, ResampBuffer, ResampBufferPos * 2 * sizeof(int16));
|
||||
espec->SoundBufSize = ResampBufferPos;
|
||||
ResampBufferPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
spx_uint32_t in_len;
|
||||
spx_uint32_t out_len;
|
||||
|
||||
if(PrevRate != (uint32)espec->SoundRate)
|
||||
{
|
||||
speex_resampler_set_rate(resampler, 44100, (uint32)espec->SoundRate);
|
||||
PrevRate = (uint32)espec->SoundRate;
|
||||
}
|
||||
|
||||
in_len = ResampBufferPos;
|
||||
out_len = 524288; // FIXME, real size.
|
||||
|
||||
speex_resampler_process_interleaved_int(resampler, (const spx_int16_t *)ResampBuffer, &in_len, (spx_int16_t *)espec->SoundBuf, &out_len);
|
||||
|
||||
assert(in_len <= ResampBufferPos);
|
||||
|
||||
if((ResampBufferPos - in_len) > 0)
|
||||
memmove(ResampBuffer, ResampBuffer + in_len, (ResampBufferPos - in_len) * sizeof(int16) * 2);
|
||||
|
||||
ResampBufferPos -= in_len;
|
||||
|
||||
//printf("%d\n", ResampBufferPos);
|
||||
assert((ResampBufferPos + 588) <= (sizeof(ResampBuffer) / sizeof(int16) / 2));
|
||||
|
||||
espec->SoundBufSize = out_len;
|
||||
}
|
||||
}
|
||||
// for(int i = 0; i < espec->SoundBufSize * 2; i++)
|
||||
// espec->SoundBuf[i] = (rand() & 0x7FFF) - 0x4000; //(rand() * 192) >> 8
|
||||
|
||||
if(!espec->skip)
|
||||
{
|
||||
char tmpbuf[256];
|
||||
const MDFN_PixelFormat &format = espec->surface->format;
|
||||
uint32 *pixels = espec->surface->pixels;
|
||||
uint32 text_color = format.MakeColor(0xE0, 0xE0, 0xE0);
|
||||
uint32 text_shadow_color = format.MakeColor(0x20, 0x20, 0x20);
|
||||
uint32 cur_sector = PlaySector;
|
||||
|
||||
espec->DisplayRect.x = 0;
|
||||
espec->DisplayRect.y = 0;
|
||||
|
||||
espec->DisplayRect.w = 320;
|
||||
espec->DisplayRect.h = 240;
|
||||
|
||||
espec->surface->Fill(0, 0, 0, 0);
|
||||
|
||||
{
|
||||
uint32 color_table[256];
|
||||
|
||||
for(int i = 0; i < 170; i++)
|
||||
{
|
||||
int m = 255 * i / 170;
|
||||
|
||||
color_table[i] = format.MakeColor(m, 0, m);
|
||||
|
||||
//printf("%d %d %08x\n", m, i, color_table[i]);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 588; i++)
|
||||
{
|
||||
int32 unip_samp;
|
||||
int32 next_unip_samp;
|
||||
|
||||
unip_samp = ((CDDABuffer[i * 2 + 0] + CDDABuffer[i * 2 + 1]) >> 1) + 32768;
|
||||
next_unip_samp = ((CDDABuffer[(i * 2 + 2) % 1176] + CDDABuffer[(i * 2 + 3) % 1176]) >> 1) + 32768;
|
||||
|
||||
for(int osi = 0; osi < oversample; osi++)
|
||||
{
|
||||
double sample;
|
||||
int x;
|
||||
int y;
|
||||
double x_raw, y_raw;
|
||||
double x_raw2, y_raw2;
|
||||
double x_raw_prime, y_raw_prime;
|
||||
|
||||
sample = (double)(unip_samp * (oversample - osi) + next_unip_samp * osi) / (oversample * 65536);
|
||||
|
||||
int32 theta_i = (int64)65536 * (i * oversample + osi) / (oversample * 588);
|
||||
int32 theta_i_alt = (int64)65536 * (i * oversample + osi) / (oversample * 588);
|
||||
|
||||
double radius = sin_lut[(lobes * theta_i) & 0xFFFF]; // * sin_lut[(16384 + (theta_i)) & 0xFFFF];
|
||||
double radius2 = sin_lut[(lobes * (theta_i + 1)) & 0xFFFF]; // * sin_lut[(16384 + ((theta_i + 1))) & 0xFFFF];
|
||||
|
||||
x_raw = radius * sin_lut[(16384 + theta_i_alt) & 0xFFFF];
|
||||
y_raw = radius * sin_lut[theta_i_alt & 0xFFFF];
|
||||
|
||||
x_raw2 = radius2 * sin_lut[(16384 + theta_i_alt + 1) & 0xFFFF];
|
||||
y_raw2 = radius2 * sin_lut[(theta_i_alt + 1) & 0xFFFF];
|
||||
|
||||
// Approximation, of course.
|
||||
x_raw_prime = (x_raw2 - x_raw) / (1 * M_PI * 2 / 65536);
|
||||
y_raw_prime = (y_raw2 - y_raw) / (1 * M_PI * 2 / 65536);
|
||||
|
||||
// printf("%f %f\n", y_raw_prime, sin_lut[(16384 + lobes * theta_i_alt) & 0xFFFF] + sin_lut[(16384 + theta_i_alt) & 0xFFFF]);
|
||||
|
||||
if(x_raw_prime || y_raw_prime)
|
||||
{
|
||||
x_raw_prime = x_raw_prime / sqrt(x_raw_prime * x_raw_prime + y_raw_prime * y_raw_prime);
|
||||
y_raw_prime = y_raw_prime / sqrt(x_raw_prime * x_raw_prime + y_raw_prime * y_raw_prime);
|
||||
}
|
||||
|
||||
x_raw += (sample - 0.5) * y_raw_prime / 2;
|
||||
y_raw += (sample - 0.5) * -x_raw_prime / 2;
|
||||
|
||||
x = 160 + 100 * x_raw;
|
||||
y = 120 + 100 * y_raw;
|
||||
|
||||
if((x >= 0 && x < 320) && (y >= 0 && y < 240))
|
||||
pixels[x + y * espec->surface->pitch32] = format.MakeColor(255, 255, 255); //color_table[(int)(sample * 150)]; //x * x + y * y; //format.MakeColor(sample * 255, 0, sample * 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TOC toc;
|
||||
|
||||
(*cdifs)[AudioTrackList[CurrentATLI].disc]->ReadTOC(&toc);
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Disc: %d/%d", AudioTrackList[CurrentATLI].disc + 1, cdifs->size());
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Track: %d/%d", AudioTrackList[CurrentATLI].track, toc.last_track);
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Sector: %d/%d", cur_sector, toc.tracks[100].lba - 1);
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
}
|
||||
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)"SubQ", text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
//trio_snprintf(tmpbuf, 256, "Q-Mode: %01x", SubQBuf[1][0] & 0xF);
|
||||
//DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
//pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Track: %d", BCD_to_U8(SubQBuf[1][1]));
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Index: %d", BCD_to_U8(SubQBuf[1][2]));
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Relative: %02d:%02d:%02d", BCD_to_U8(SubQBuf[1][3]), BCD_to_U8(SubQBuf[1][4]), BCD_to_U8(SubQBuf[1][5]));
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
|
||||
trio_snprintf(tmpbuf, 256, "Absolute: %02d:%02d:%02d", BCD_to_U8(SubQBuf[1][7]), BCD_to_U8(SubQBuf[1][8]), BCD_to_U8(SubQBuf[1][9]));
|
||||
DrawTextTransShadow(pixels, espec->surface->pitch32 * 4, 320, (UTF8 *)tmpbuf, text_color, text_shadow_color, 0, MDFN_FONT_9x18_18x18);
|
||||
pixels += 22 * espec->surface->pitch32;
|
||||
}
|
||||
|
||||
if(PlayMode != PLAYMODE_STOP && PlayMode != PLAYMODE_PAUSE)
|
||||
{
|
||||
const int scan_amount = 4; //16;
|
||||
|
||||
if(new_controller & 0x40)
|
||||
PlaySector += scan_amount;
|
||||
else if(new_controller & 0x80)
|
||||
PlaySector -= scan_amount;
|
||||
else
|
||||
PlaySector++;
|
||||
}
|
||||
|
||||
if(!(last_controller & 0x1) && (new_controller & 1))
|
||||
{
|
||||
PlayMode = (PlayMode == PLAYMODE_PLAY) ? PLAYMODE_PAUSE : PLAYMODE_PLAY;
|
||||
}
|
||||
|
||||
if(!(last_controller & 0x2) && (new_controller & 2)) // Stop
|
||||
{
|
||||
PlayMode = PLAYMODE_STOP;
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
|
||||
if(!(last_controller & 0x4) && (new_controller & 4))
|
||||
{
|
||||
if(CurrentATLI < (AudioTrackList.size() - 1))
|
||||
CurrentATLI++;
|
||||
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
|
||||
if(!(last_controller & 0x8) && (new_controller & 8))
|
||||
{
|
||||
if(CurrentATLI)
|
||||
CurrentATLI--;
|
||||
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
|
||||
if(!(last_controller & 0x10) && (new_controller & 0x10))
|
||||
{
|
||||
CurrentATLI = std::min<int>(CurrentATLI + 10, AudioTrackList.size() - 1);
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
|
||||
if(!(last_controller & 0x20) && (new_controller & 0x20))
|
||||
{
|
||||
CurrentATLI = std::max<int>(CurrentATLI - 10, 0);
|
||||
PlaySector = AudioTrackList[CurrentATLI].lba;
|
||||
}
|
||||
|
||||
|
||||
last_controller = new_controller;
|
||||
}
|
||||
|
||||
static const FileExtensionSpecStruct KnownExtensions[] =
|
||||
{
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static void SetInput(int port, const char *type, void *ptr)
|
||||
{
|
||||
controller_ptr = (uint8 *)ptr;
|
||||
}
|
||||
|
||||
static void DoSimpleCommand(int cmd)
|
||||
{
|
||||
switch(cmd)
|
||||
{
|
||||
case MDFNNPCMD_POWER:
|
||||
case MDFNNPCMD_RESET: break;
|
||||
}
|
||||
}
|
||||
|
||||
static MDFNSetting CDPlaySettings[] =
|
||||
{
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static const InputDeviceInputInfoStruct IDII[] =
|
||||
{
|
||||
{ "play_pause", "Play/Pause", 0, IDIT_BUTTON, NULL },
|
||||
{ "stop", "Stop", 1, IDIT_BUTTON, NULL },
|
||||
{ "next_track", "Next Track", 2, IDIT_BUTTON, NULL },
|
||||
{ "previous_track", "Previous Track", 3, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "next_track_10", "Next Track 10", 4, IDIT_BUTTON, NULL },
|
||||
{ "previous_track_10", "Previous Track 10", 5, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "scan_forward", "Scan Forward", 6, IDIT_BUTTON, NULL },
|
||||
{ "scan_reverse", "Scan Reverse", 7, IDIT_BUTTON, NULL },
|
||||
|
||||
//{ "reverse_seek", "Reverse Seek", 1, IDIT_BUTTON, NULL },
|
||||
//{ "forward_seek", "Forward Seek", 2, IDIT_BUTTON, NULL },
|
||||
//{ "fast_reverse_seek", "Fast Reverse Seek", 3, IDIT_BUTTON, NULL },
|
||||
//{ "fast_forward_seek", "Fast Forward Seek", 4, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
static InputDeviceInfoStruct InputDeviceInfo[] =
|
||||
{
|
||||
{
|
||||
"controller",
|
||||
"Controller",
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
IDII,
|
||||
}
|
||||
};
|
||||
|
||||
static const InputPortInfoStruct PortInfo[] =
|
||||
{
|
||||
{ "builtin", "Built-In", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo }
|
||||
};
|
||||
|
||||
static InputInfoStruct InputInfo =
|
||||
{
|
||||
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
|
||||
PortInfo
|
||||
};
|
||||
}
|
||||
|
||||
using namespace MDFN_IEN_CDPLAY;
|
||||
|
||||
MDFNGI EmulatedCDPlay =
|
||||
{
|
||||
"cdplay",
|
||||
"Mednafen Test CD-DA Player",
|
||||
KnownExtensions,
|
||||
MODPRIO_INTERNAL_EXTRA_LOW,
|
||||
NULL, // Debug info
|
||||
&InputInfo, //
|
||||
NULL,
|
||||
NULL,
|
||||
LoadCD,
|
||||
TestMagicCD,
|
||||
CloseGame,
|
||||
NULL,
|
||||
NULL, // Layer names, null-delimited
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
false,
|
||||
NULL, //StateAction,
|
||||
Emulate,
|
||||
SetInput,
|
||||
DoSimpleCommand,
|
||||
CDPlaySettings,
|
||||
MDFN_MASTERCLOCK_FIXED(44100),
|
||||
75 * 65536 * 256,
|
||||
false, // Multires possible?
|
||||
|
||||
320, // lcm_width
|
||||
240, // lcm_height
|
||||
NULL, // Dummy
|
||||
|
||||
|
||||
320, // Nominal width
|
||||
240, // Nominal height
|
||||
512, // Framebuffer width
|
||||
256, // Framebuffer height
|
||||
|
||||
2, // Number of output sound channels
|
||||
};
|
||||
|
@ -1,13 +0,0 @@
|
||||
mednafen_SOURCES += cputest/cputest.c
|
||||
|
||||
if ARCH_X86
|
||||
mednafen_SOURCES += cputest/x86_cpu.c
|
||||
endif
|
||||
|
||||
if ARCH_POWERPC
|
||||
mednafen_SOURCES += cputest/ppc_cpu.c
|
||||
endif
|
||||
|
||||
#if ARCH_ARM
|
||||
#mednafen_SOURCES += cputest/arm_cpu.c
|
||||
#endif
|
@ -1,3 +0,0 @@
|
||||
modified slightly for usage in Mednafen
|
||||
|
||||
ARM stuff not compiled in, for now.
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "cputest.h"
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
int ff_get_cpu_flags_arm(void)
|
||||
{
|
||||
|
||||
return 0;
|
||||
// return HAVE_IWMMXT * CPUTEST_FLAG_IWMMXT;
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "cputest.h"
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
static int flags, checked = 0;
|
||||
|
||||
|
||||
void cputest_force_flags(int arg)
|
||||
{
|
||||
flags = arg;
|
||||
checked = 1;
|
||||
}
|
||||
|
||||
int cputest_get_flags(void)
|
||||
{
|
||||
if (checked)
|
||||
return flags;
|
||||
|
||||
// if (ARCH_ARM) flags = ff_get_cpu_flags_arm();
|
||||
#if ARCH_POWERPC
|
||||
flags = ff_get_cpu_flags_ppc();
|
||||
#endif
|
||||
|
||||
#if ARCH_X86
|
||||
flags = ff_get_cpu_flags_x86();
|
||||
#endif
|
||||
|
||||
checked = 1;
|
||||
return flags;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//#ifdef TEST
|
||||
|
||||
#undef printf
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int cpu_flags = av_get_cpu_flags();
|
||||
|
||||
printf("cpu_flags = 0x%08X\n", cpu_flags);
|
||||
printf("cpu_flags = %s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
||||
#if ARCH_ARM
|
||||
cpu_flags & CPUTEST_FLAG_IWMMXT ? "IWMMXT " : "",
|
||||
#elif ARCH_POWERPC
|
||||
cpu_flags & CPUTEST_FLAG_ALTIVEC ? "ALTIVEC " : "",
|
||||
#elif ARCH_X86
|
||||
cpu_flags & CPUTEST_FLAG_MMX ? "MMX " : "",
|
||||
cpu_flags & CPUTEST_FLAG_MMX2 ? "MMX2 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE ? "SSE " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE2 ? "SSE2 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE2SLOW ? "SSE2(slow) " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE3 ? "SSE3 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE3SLOW ? "SSE3(slow) " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSSE3 ? "SSSE3 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_ATOM ? "Atom " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE4 ? "SSE4.1 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE42 ? "SSE4.2 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_AVX ? "AVX " : "",
|
||||
cpu_flags & CPUTEST_FLAG_3DNOW ? "3DNow " : "",
|
||||
cpu_flags & CPUTEST_FLAG_3DNOWEXT ? "3DNowExt " : "");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <machine/cpu.h>
|
||||
#elif defined(__AMIGAOS4__)
|
||||
#include <exec/exec.h>
|
||||
#include <interfaces/exec.h>
|
||||
#include <proto/exec.h>
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#include "cputest.h"
|
||||
|
||||
/**
|
||||
* This function MAY rely on signal() or fork() in order to make sure AltiVec
|
||||
* is present.
|
||||
*/
|
||||
int ff_get_cpu_flags_ppc(void)
|
||||
{
|
||||
//#if HAVE_ALTIVEC
|
||||
#if ARCH_POWERPC_ALTIVEC
|
||||
#ifdef __AMIGAOS4__
|
||||
ULONG result = 0;
|
||||
extern struct ExecIFace *IExec;
|
||||
|
||||
IExec->GetCPUInfoTags(GCIT_VectorUnit, &result, TAG_DONE);
|
||||
if (result == VECTORTYPE_ALTIVEC)
|
||||
return CPUTEST_FLAG_ALTIVEC;
|
||||
return 0;
|
||||
#elif defined(__APPLE__) || defined(__OpenBSD__)
|
||||
#ifdef __OpenBSD__
|
||||
int sels[2] = {CTL_MACHDEP, CPU_ALTIVEC};
|
||||
#else
|
||||
int sels[2] = {CTL_HW, HW_VECTORUNIT};
|
||||
#endif
|
||||
int has_vu = 0;
|
||||
size_t len = sizeof(has_vu);
|
||||
int err;
|
||||
|
||||
err = sysctl(sels, 2, &has_vu, &len, NULL, 0);
|
||||
|
||||
if (err == 0)
|
||||
return has_vu ? CPUTEST_FLAG_ALTIVEC : 0;
|
||||
return 0;
|
||||
#elif CONFIG_RUNTIME_CPUDETECT
|
||||
int proc_ver;
|
||||
// Support of mfspr PVR emulation added in Linux 2.6.17.
|
||||
__asm__ volatile("mfspr %0, 287" : "=r" (proc_ver));
|
||||
proc_ver >>= 16;
|
||||
if (proc_ver & 0x8000 ||
|
||||
proc_ver == 0x000c ||
|
||||
proc_ver == 0x0039 || proc_ver == 0x003c ||
|
||||
proc_ver == 0x0044 || proc_ver == 0x0045 ||
|
||||
proc_ver == 0x0070)
|
||||
return CPUTEST_FLAG_ALTIVEC;
|
||||
return 0;
|
||||
#else
|
||||
// Since we were compiled for AltiVec, just assume we have it
|
||||
// until someone comes up with a proper way (not involving signal hacks).
|
||||
return CPUTEST_FLAG_ALTIVEC;
|
||||
#endif /* __AMIGAOS4__ */
|
||||
#endif /* HAVE_ALTIVEC */
|
||||
return 0;
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* CPU detection code, extracted from mmx.h
|
||||
* (c)1997-99 by H. Dietz and R. Fisher
|
||||
* Converted to C and improved by Fabrice Bellard.
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "x86_cpu.h"
|
||||
#include "cputest.h"
|
||||
|
||||
/* ebx saving is necessary for PIC. gcc seems unable to see it alone */
|
||||
#define cpuid(index,eax,ebx,ecx,edx)\
|
||||
__asm__ volatile\
|
||||
("mov %%"REG_b", %%"REG_S"\n\t"\
|
||||
"cpuid\n\t"\
|
||||
"xchg %%"REG_b", %%"REG_S\
|
||||
: "=a" (eax), "=S" (ebx),\
|
||||
"=c" (ecx), "=d" (edx)\
|
||||
: "0" (index));
|
||||
|
||||
#define xgetbv(index,eax,edx) \
|
||||
__asm__ (".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c" (index))
|
||||
|
||||
/* Function to test if multimedia instructions are supported... */
|
||||
int ff_get_cpu_flags_x86(void)
|
||||
{
|
||||
int rval = 0;
|
||||
int eax, ebx, ecx, edx;
|
||||
int max_std_level, max_ext_level, std_caps=0, ext_caps=0;
|
||||
int family=0, model=0;
|
||||
union { int i[3]; char c[12]; } vendor;
|
||||
|
||||
#if ARCH_X86_32
|
||||
x86_reg a, c;
|
||||
__asm__ volatile (
|
||||
/* See if CPUID instruction is supported ... */
|
||||
/* ... Get copies of EFLAGS into eax and ecx */
|
||||
"pushfl\n\t"
|
||||
"pop %0\n\t"
|
||||
"mov %0, %1\n\t"
|
||||
|
||||
/* ... Toggle the ID bit in one copy and store */
|
||||
/* to the EFLAGS reg */
|
||||
"xor $0x200000, %0\n\t"
|
||||
"push %0\n\t"
|
||||
"popfl\n\t"
|
||||
|
||||
/* ... Get the (hopefully modified) EFLAGS */
|
||||
"pushfl\n\t"
|
||||
"pop %0\n\t"
|
||||
: "=a" (a), "=c" (c)
|
||||
:
|
||||
: "cc"
|
||||
);
|
||||
|
||||
if (a == c)
|
||||
return 0; /* CPUID not supported */
|
||||
#endif
|
||||
|
||||
cpuid(0, max_std_level, vendor.i[0], vendor.i[2], vendor.i[1]);
|
||||
|
||||
if(max_std_level >= 1){
|
||||
cpuid(1, eax, ebx, ecx, std_caps);
|
||||
family = ((eax>>8)&0xf) + ((eax>>20)&0xff);
|
||||
model = ((eax>>4)&0xf) + ((eax>>12)&0xf0);
|
||||
if (std_caps & (1<<23))
|
||||
rval |= CPUTEST_FLAG_MMX;
|
||||
if (std_caps & (1<<25))
|
||||
rval |= CPUTEST_FLAG_MMX2
|
||||
//#if HAVE_SSE
|
||||
| CPUTEST_FLAG_SSE;
|
||||
if (std_caps & (1<<26))
|
||||
rval |= CPUTEST_FLAG_SSE2;
|
||||
if (ecx & 1)
|
||||
rval |= CPUTEST_FLAG_SSE3;
|
||||
if (ecx & 0x00000200 )
|
||||
rval |= CPUTEST_FLAG_SSSE3;
|
||||
if (ecx & 0x00080000 )
|
||||
rval |= CPUTEST_FLAG_SSE4;
|
||||
if (ecx & 0x00100000 )
|
||||
rval |= CPUTEST_FLAG_SSE42;
|
||||
//#if HAVE_AVX
|
||||
/* Check OXSAVE and AVX bits */
|
||||
if ((ecx & 0x18000000) == 0x18000000) {
|
||||
/* Check for OS support */
|
||||
xgetbv(0, eax, edx);
|
||||
if ((eax & 0x6) == 0x6)
|
||||
rval |= CPUTEST_FLAG_AVX;
|
||||
}
|
||||
//#endif
|
||||
//#endif
|
||||
;
|
||||
}
|
||||
|
||||
cpuid(0x80000000, max_ext_level, ebx, ecx, edx);
|
||||
|
||||
if(max_ext_level >= 0x80000001){
|
||||
cpuid(0x80000001, eax, ebx, ecx, ext_caps);
|
||||
if (ext_caps & (1<<31))
|
||||
rval |= CPUTEST_FLAG_3DNOW;
|
||||
if (ext_caps & (1<<30))
|
||||
rval |= CPUTEST_FLAG_3DNOWEXT;
|
||||
if (ext_caps & (1<<23))
|
||||
rval |= CPUTEST_FLAG_MMX;
|
||||
if (ext_caps & (1<<22))
|
||||
rval |= CPUTEST_FLAG_MMX2;
|
||||
|
||||
/* Allow for selectively disabling SSE2 functions on AMD processors
|
||||
with SSE2 support but not SSE4a. This includes Athlon64, some
|
||||
Opteron, and some Sempron processors. MMX, SSE, or 3DNow! are faster
|
||||
than SSE2 often enough to utilize this special-case flag.
|
||||
CPUTEST_FLAG_SSE2 and CPUTEST_FLAG_SSE2SLOW are both set in this case
|
||||
so that SSE2 is used unless explicitly disabled by checking
|
||||
CPUTEST_FLAG_SSE2SLOW. */
|
||||
if (!strncmp(vendor.c, "AuthenticAMD", 12) &&
|
||||
rval & CPUTEST_FLAG_SSE2 && !(ecx & 0x00000040)) {
|
||||
rval |= CPUTEST_FLAG_SSE2SLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncmp(vendor.c, "GenuineIntel", 12)) {
|
||||
if (family == 6 && (model == 9 || model == 13 || model == 14)) {
|
||||
/* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and 6/14 (core1 "yonah")
|
||||
* theoretically support sse2, but it's usually slower than mmx,
|
||||
* so let's just pretend they don't. CPUTEST_FLAG_SSE2 is disabled and
|
||||
* CPUTEST_FLAG_SSE2SLOW is enabled so that SSE2 is not used unless
|
||||
* explicitly enabled by checking CPUTEST_FLAG_SSE2SLOW. The same
|
||||
* situation applies for CPUTEST_FLAG_SSE3 and CPUTEST_FLAG_SSE3SLOW. */
|
||||
if (rval & CPUTEST_FLAG_SSE2) rval ^= CPUTEST_FLAG_SSE2SLOW|CPUTEST_FLAG_SSE2;
|
||||
if (rval & CPUTEST_FLAG_SSE3) rval ^= CPUTEST_FLAG_SSE3SLOW|CPUTEST_FLAG_SSE3;
|
||||
}
|
||||
/* The Atom processor has SSSE3 support, which is useful in many cases,
|
||||
* but sometimes the SSSE3 version is slower than the SSE2 equivalent
|
||||
* on the Atom, but is generally faster on other processors supporting
|
||||
* SSSE3. This flag allows for selectively disabling certain SSSE3
|
||||
* functions on the Atom. */
|
||||
if (family == 6 && model == 28)
|
||||
rval |= CPUTEST_FLAG_ATOM;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef AVUTIL_X86_CPU_H
|
||||
#define AVUTIL_X86_CPU_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if ARCH_X86_64
|
||||
# define OPSIZE "q"
|
||||
# define REG_a "rax"
|
||||
# define REG_b "rbx"
|
||||
# define REG_c "rcx"
|
||||
# define REG_d "rdx"
|
||||
# define REG_D "rdi"
|
||||
# define REG_S "rsi"
|
||||
# define PTR_SIZE "8"
|
||||
typedef int64_t x86_reg;
|
||||
|
||||
# define REG_SP "rsp"
|
||||
# define REG_BP "rbp"
|
||||
# define REGBP rbp
|
||||
# define REGa rax
|
||||
# define REGb rbx
|
||||
# define REGc rcx
|
||||
# define REGd rdx
|
||||
# define REGSP rsp
|
||||
|
||||
#elif ARCH_X86_32
|
||||
|
||||
# define OPSIZE "l"
|
||||
# define REG_a "eax"
|
||||
# define REG_b "ebx"
|
||||
# define REG_c "ecx"
|
||||
# define REG_d "edx"
|
||||
# define REG_D "edi"
|
||||
# define REG_S "esi"
|
||||
# define PTR_SIZE "4"
|
||||
typedef int32_t x86_reg;
|
||||
|
||||
# define REG_SP "esp"
|
||||
# define REG_BP "ebp"
|
||||
# define REGBP ebp
|
||||
# define REGa eax
|
||||
# define REGb ebx
|
||||
# define REGc ecx
|
||||
# define REGd edx
|
||||
# define REGSP esp
|
||||
#else
|
||||
typedef int x86_reg;
|
||||
#endif
|
||||
|
||||
#define HAVE_7REGS (ARCH_X86_64 || (HAVE_EBX_AVAILABLE && HAVE_EBP_AVAILABLE))
|
||||
#define HAVE_6REGS (ARCH_X86_64 || (HAVE_EBX_AVAILABLE || HAVE_EBP_AVAILABLE))
|
||||
|
||||
#if ARCH_X86_64 && defined(PIC)
|
||||
# define BROKEN_RELOCATIONS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If gcc is not set to support sse (-msse) it will not accept xmm registers
|
||||
* in the clobber list for inline asm. XMM_CLOBBERS takes a list of xmm
|
||||
* registers to be marked as clobbered and evaluates to nothing if they are
|
||||
* not supported, or to the list itself if they are supported. Since a clobber
|
||||
* list may not be empty, XMM_CLOBBERS_ONLY should be used if the xmm
|
||||
* registers are the only in the clobber list.
|
||||
* For example a list with "eax" and "xmm0" as clobbers should become:
|
||||
* : XMM_CLOBBERS("xmm0",) "eax"
|
||||
* and a list with only "xmm0" should become:
|
||||
* XMM_CLOBBERS_ONLY("xmm0")
|
||||
*/
|
||||
#if HAVE_XMM_CLOBBERS
|
||||
# define XMM_CLOBBERS(...) __VA_ARGS__
|
||||
# define XMM_CLOBBERS_ONLY(...) : __VA_ARGS__
|
||||
#else
|
||||
# define XMM_CLOBBERS(...)
|
||||
# define XMM_CLOBBERS_ONLY(...)
|
||||
#endif
|
||||
|
||||
#endif /* AVUTIL_X86_CPU_H */
|
@ -6,9 +6,7 @@
|
||||
#include "git.h"
|
||||
#include "settings-driver.h"
|
||||
#include "mednafen-driver.h"
|
||||
#include "netplay-driver.h"
|
||||
#include "state-driver.h"
|
||||
#include "movie-driver.h"
|
||||
#include "mempatcher-driver.h"
|
||||
#include "video-driver.h"
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#include "general.h"
|
||||
#include "state.h"
|
||||
#include "movie.h"
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
@ -215,248 +214,6 @@ static std::string EvalPathFS(const std::string &fstring, /*const (won't work be
|
||||
return(ret);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void CreateMissingDirs(const char *path)
|
||||
{
|
||||
const char *s = path;
|
||||
bool first_psep = true;
|
||||
char last_char = 0;
|
||||
const char char_test1 = '/', char_test2 = '/';
|
||||
|
||||
|
||||
while(*s)
|
||||
{
|
||||
if(*s == char_test1 || *s == char_test2)
|
||||
{
|
||||
if(last_char != *s) //char_test1 && last_char != char_test2)
|
||||
{
|
||||
if(!first_psep)
|
||||
{
|
||||
char tmpbuf[(s - path) + 1];
|
||||
tmpbuf[s - path] = 0;
|
||||
strncpy(tmpbuf, path, s - path);
|
||||
|
||||
puts(tmpbuf);
|
||||
//MDFN_mkdir(tmpbuf, S_IRWXU);
|
||||
}
|
||||
}
|
||||
|
||||
first_psep = false;
|
||||
}
|
||||
last_char = *s;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
std::string MDFN_MakeFName(MakeFName_Type type, int id1, const char *cd1)
|
||||
{
|
||||
char tmp_path[4096];
|
||||
char numtmp[64];
|
||||
struct stat tmpstat;
|
||||
string eff_dir;
|
||||
FSMap fmap;
|
||||
|
||||
fmap['b'] = BaseDirectory;
|
||||
fmap['z'] = std::string(PSS);
|
||||
|
||||
if(MDFNGameInfo)
|
||||
{
|
||||
fmap['d'] = FileBaseDirectory;
|
||||
fmap['f'] = FileBase;
|
||||
fmap['F'] = FileBase; // If game is a CD, and the CD is recognized as being part of a multi-CD set, then this
|
||||
// will be replaced with MDFNGameInfo->shortname
|
||||
|
||||
fmap['m'] = md5_context::asciistr(MDFNGameInfo->MD5, 0); // MD5 hash of the currently loaded game ONLY.
|
||||
|
||||
fmap['M'] = ""; // One with this empty, if file not found, then fill with MD5 hash of the currently loaded game,
|
||||
// or the MD5 gameset hash for certain CD games, followed by a period and go with that result.
|
||||
// Note: The MD5-less result is skipped if the CD is part of a recognized multi-CD set.
|
||||
fmap['e'] = FileExt;
|
||||
fmap['s'] = MDFNGameInfo->shortname;
|
||||
|
||||
fmap['p'] = "";
|
||||
|
||||
|
||||
fmap['x'] = ""; // Default extension(without period)
|
||||
fmap['X'] = ""; // A merging of x and p
|
||||
|
||||
if(MDFNGameInfo->GameSetMD5Valid)
|
||||
{
|
||||
fmap['M'] = md5_context::asciistr(MDFNGameInfo->GameSetMD5, 0) + std::string(".");
|
||||
fmap['F'] = MDFNGameInfo->shortname;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//printf("%s\n", EvalPathFS(std::string("%f.%m.sav"), fmap).c_str());
|
||||
|
||||
switch(type)
|
||||
{
|
||||
default: tmp_path[0] = 0;
|
||||
break;
|
||||
|
||||
case MDFNMKF_MOVIE:
|
||||
case MDFNMKF_STATE:
|
||||
case MDFNMKF_SAV:
|
||||
{
|
||||
std::string dir, fstring, fpath;
|
||||
|
||||
if(type == MDFNMKF_MOVIE)
|
||||
{
|
||||
dir = MDFN_GetSettingS("filesys.path_movie");
|
||||
fstring = MDFN_GetSettingS("filesys.fname_movie");
|
||||
fmap['x'] = "mcm";
|
||||
}
|
||||
else if(type == MDFNMKF_STATE)
|
||||
{
|
||||
dir = MDFN_GetSettingS("filesys.path_state");
|
||||
fstring = MDFN_GetSettingS("filesys.fname_state");
|
||||
fmap['x'] = "mcs";
|
||||
}
|
||||
else if(type == MDFNMKF_SAV)
|
||||
{
|
||||
dir = MDFN_GetSettingS("filesys.path_sav");
|
||||
fstring = MDFN_GetSettingS("filesys.fname_sav");
|
||||
fmap['x'] = std::string(cd1);
|
||||
}
|
||||
|
||||
fmap['X'] = fmap['x'];
|
||||
|
||||
if(type != MDFNMKF_SAV)
|
||||
{
|
||||
snprintf(numtmp, sizeof(numtmp), "%d", id1);
|
||||
fmap['p'] = std::string(numtmp);
|
||||
}
|
||||
|
||||
if(fmap['X'].size() > 1 && fmap['p'].size())
|
||||
fmap['X'] = fmap['X'].erase(fmap['X'].size() - 1) + fmap['p'];
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
fpath = EvalPathFS(fstring, fmap);
|
||||
|
||||
if(!IsAbsolutePath(fpath))
|
||||
{
|
||||
if(!IsAbsolutePath(dir))
|
||||
dir = BaseDirectory + std::string(PSS) + dir;
|
||||
|
||||
fpath = dir + std::string(PSS) + fpath;
|
||||
}
|
||||
|
||||
if(stat(fpath.c_str(), &tmpstat) == -1)
|
||||
fmap['M'] = md5_context::asciistr(MDFNGameInfo->MD5, 0) + std::string(".");
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return(fpath);
|
||||
}
|
||||
|
||||
case MDFNMKF_SNAP_DAT:
|
||||
case MDFNMKF_SNAP:
|
||||
{
|
||||
std::string dir = MDFN_GetSettingS("filesys.path_snap");
|
||||
std::string fstring = MDFN_GetSettingS("filesys.fname_snap");
|
||||
std::string fpath;
|
||||
|
||||
snprintf(numtmp, sizeof(numtmp), "%04d", id1);
|
||||
|
||||
fmap['p'] = std::string(numtmp);
|
||||
|
||||
if(cd1)
|
||||
fmap['x'] = std::string(cd1);
|
||||
|
||||
if(type == MDFNMKF_SNAP_DAT)
|
||||
{
|
||||
fmap['p'] = std::string("counter");
|
||||
fmap['x'] = std::string("txt");
|
||||
}
|
||||
fpath = EvalPathFS(fstring, fmap);
|
||||
if(!IsAbsolutePath(fpath))
|
||||
{
|
||||
if(!IsAbsolutePath(dir))
|
||||
dir = BaseDirectory + std::string(PSS) + dir;
|
||||
|
||||
fpath = dir + std::string(PSS) + fpath;
|
||||
}
|
||||
return(fpath);
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNMKF_CHEAT_TMP:
|
||||
case MDFNMKF_CHEAT:
|
||||
{
|
||||
std::string overpath = MDFN_GetSettingS("filesys.path_cheat");
|
||||
|
||||
if(IsAbsolutePath(overpath))
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s.%scht",overpath.c_str(), MDFNGameInfo->shortname, (type == MDFNMKF_CHEAT_TMP) ? "tmp" : "");
|
||||
else
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s" PSS "%s.%scht", BaseDirectory.c_str(), overpath.c_str(), MDFNGameInfo->shortname, (type == MDFNMKF_CHEAT_TMP) ? "tmp" : "");
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNMKF_AUX: if(IsAbsolutePath(cd1))
|
||||
trio_snprintf(tmp_path, 4096, "%s", (char *)cd1);
|
||||
else
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s", FileBaseDirectory.c_str(), (char *)cd1);
|
||||
break;
|
||||
|
||||
case MDFNMKF_IPS: trio_snprintf(tmp_path, 4096, "%s" PSS "%s%s.ips", FileBaseDirectory.c_str(), FileBase.c_str(), FileExt.c_str());
|
||||
break;
|
||||
|
||||
case MDFNMKF_FIRMWARE:
|
||||
{
|
||||
std::string overpath = MDFN_GetSettingS("filesys.path_firmware");
|
||||
|
||||
if(IsAbsolutePath(cd1))
|
||||
{
|
||||
trio_snprintf(tmp_path, 4096, "%s", cd1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IsAbsolutePath(overpath))
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s",overpath.c_str(), cd1);
|
||||
else
|
||||
{
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s" PSS "%s", BaseDirectory.c_str(), overpath.c_str(), cd1);
|
||||
|
||||
// For backwards-compatibility with < 0.9.0
|
||||
if(stat(tmp_path,&tmpstat) == -1)
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s", BaseDirectory.c_str(), cd1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNMKF_PALETTE:
|
||||
{
|
||||
std::string overpath = MDFN_GetSettingS("filesys.path_palette");
|
||||
|
||||
if(IsAbsolutePath(overpath))
|
||||
eff_dir = overpath;
|
||||
else
|
||||
eff_dir = std::string(BaseDirectory) + std::string(PSS) + overpath;
|
||||
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s.pal", eff_dir.c_str(), FileBase.c_str());
|
||||
|
||||
if(stat(tmp_path,&tmpstat) == -1 && errno == ENOENT)
|
||||
{
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s.%s.pal", eff_dir.c_str(), FileBase.c_str(), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
|
||||
|
||||
if(stat(tmp_path, &tmpstat) == -1 && errno == ENOENT)
|
||||
trio_snprintf(tmp_path, 4096, "%s" PSS "%s.pal", eff_dir.c_str(), cd1 ? cd1 : MDFNGameInfo->shortname);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return(tmp_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char * GetFNComponent(const char *str)
|
||||
{
|
||||
const char *tp1;
|
||||
|
@ -27,12 +27,9 @@
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
|
||||
#include "netplay.h"
|
||||
#include "netplay-driver.h"
|
||||
#include "general.h"
|
||||
|
||||
#include "state.h"
|
||||
#include "movie.h"
|
||||
#include "video.h"
|
||||
#include "video/Deinterlacer.h"
|
||||
#include "file.h"
|
||||
@ -40,9 +37,6 @@
|
||||
#include "cdrom/cdromif.h"
|
||||
#include "mempatcher.h"
|
||||
#include "compress/minilzo.h"
|
||||
#include "tests.h"
|
||||
#include "video/tblur.h"
|
||||
#include "qtrecord.h"
|
||||
#include "md5.h"
|
||||
#include "clamp.h"
|
||||
#include "Fir_Resampler.h"
|
||||
@ -53,26 +47,11 @@
|
||||
|
||||
static const char *CSD_forcemono = gettext_noop("Force monophonic sound output.");
|
||||
static const char *CSD_enable = gettext_noop("Enable (automatic) usage of this module.");
|
||||
static const char *CSD_tblur = gettext_noop("Enable video temporal blur(50/50 previous/current frame by default).");
|
||||
static const char *CSD_tblur_accum = gettext_noop("Accumulate color data rather than discarding it.");
|
||||
static const char *CSD_tblur_accum_amount = gettext_noop("Blur amount in accumulation mode, specified in percentage of accumulation buffer to mix with the current frame.");
|
||||
|
||||
static MDFNSetting_EnumList CompressorList[] =
|
||||
{
|
||||
// TODO: Actual associated numerical values are not currently used.
|
||||
{ "minilzo", -1, "MiniLZO" },
|
||||
{ "quicklz", -1, "QuickLZ" },
|
||||
{ "blz", -1, "BLZ" },
|
||||
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static const char *fname_extra = gettext_noop("See fname_format.txt for more information. Edit at your own risk.");
|
||||
|
||||
static MDFNSetting MednafenSettings[] =
|
||||
{
|
||||
{ "srwcompressor", MDFNSF_NOFLAGS, gettext_noop("Compressor to use with state rewinding"), NULL, MDFNST_ENUM, "quicklz", NULL, NULL, NULL, NULL, CompressorList },
|
||||
|
||||
{ "srwframes", MDFNSF_NOFLAGS, gettext_noop("Number of frames to keep states for when state rewinding is enabled."),
|
||||
gettext_noop("WARNING: Setting this to a large value may cause excessive RAM usage in some circumstances, such as with games that stream large volumes of data off of CDs."), MDFNST_UINT, "600", "10", "99999" },
|
||||
|
||||
@ -113,14 +92,6 @@ static MDFNSetting RenamedSettings[] =
|
||||
{ "soundvol", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "sound.volume" },
|
||||
{ "soundbufsize", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "sound.buffer_time" },
|
||||
|
||||
{ "nethost", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.host" },
|
||||
{ "netport", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.port" },
|
||||
{ "netpassword", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.password"},
|
||||
{ "netlocalplayers", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.localplayers" },
|
||||
{ "netnick", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.nick" },
|
||||
{ "netgamekey", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.gamekey" },
|
||||
{ "netsmallfont", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.smallfont" },
|
||||
|
||||
{ "frameskip", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "video.frameskip" },
|
||||
{ "vdriver", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "video.driver" },
|
||||
{ "glvsync", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "video.glvsync" },
|
||||
@ -133,13 +104,8 @@ static MDFNSetting RenamedSettings[] =
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static char *PortDeviceCache[16];
|
||||
static void *PortDataCache[16];
|
||||
static uint32 PortDataLenCache[16];
|
||||
|
||||
MDFNGI *MDFNGameInfo = NULL;
|
||||
|
||||
static WAVRecord *wavrecorder = NULL;
|
||||
static Fir_Resampler<16> ff_resampler;
|
||||
static double LastSoundMultiplier;
|
||||
|
||||
@ -169,38 +135,21 @@ void MDFNI_CloseGame(void)
|
||||
MDFNMP_Kill();
|
||||
|
||||
MDFNGameInfo = NULL;
|
||||
MDFN_StateEvilEnd();
|
||||
|
||||
for(unsigned i = 0; i < CDInterfaces.size(); i++)
|
||||
delete CDInterfaces[i];
|
||||
CDInterfaces.clear();
|
||||
}
|
||||
TBlur_Kill();
|
||||
|
||||
#ifdef WANT_DEBUGGER
|
||||
MDFNDBG_Kill();
|
||||
#endif
|
||||
|
||||
for(unsigned int x = 0; x < sizeof(PortDeviceCache) / sizeof(char *); x++)
|
||||
{
|
||||
if(PortDeviceCache[x])
|
||||
{
|
||||
free(PortDeviceCache[x]);
|
||||
PortDeviceCache[x] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(PortDataCache, 0, sizeof(PortDataCache));
|
||||
memset(PortDataLenCache, 0, sizeof(PortDataLenCache));
|
||||
memset(PortDeviceCache, 0, sizeof(PortDeviceCache));
|
||||
}
|
||||
|
||||
#ifdef WANT_PSX_EMU
|
||||
extern MDFNGI EmulatedPSX;
|
||||
#endif
|
||||
|
||||
extern MDFNGI EmulatedCDPlay;
|
||||
|
||||
std::vector<MDFNGI *> MDFNSystems;
|
||||
static std::list<MDFNGI *> MDFNSystemsPrio;
|
||||
|
||||
@ -452,15 +401,8 @@ MDFNGI *MDFNI_LoadCD(const char *force_module, const char *devicename)
|
||||
MDFNDBG_PostGameLoad();
|
||||
#endif
|
||||
|
||||
MDFNSS_CheckStates();
|
||||
|
||||
MDFN_ResetMessages(); // Save state, status messages, etc.
|
||||
|
||||
TBlur_Init();
|
||||
|
||||
MDFN_StateEvilBegin();
|
||||
|
||||
|
||||
if(MDFNGameInfo->GameType != GMT_PLAYER)
|
||||
{
|
||||
MDFN_LoadGameCheats(NULL);
|
||||
@ -673,8 +615,6 @@ MDFNGI *MDFNI_LoadGame(const char *force_module, const char *name)
|
||||
MDFNDBG_PostGameLoad();
|
||||
#endif
|
||||
|
||||
MDFNSS_CheckStates();
|
||||
|
||||
MDFN_ResetMessages(); // Save state, status messages, etc.
|
||||
|
||||
MDFN_indent(-2);
|
||||
@ -698,11 +638,6 @@ MDFNGI *MDFNI_LoadGame(const char *force_module, const char *name)
|
||||
PrevInterlaced = false;
|
||||
deint.ClearState();
|
||||
|
||||
TBlur_Init();
|
||||
|
||||
MDFN_StateEvilBegin();
|
||||
|
||||
|
||||
last_sound_rate = -1;
|
||||
memset(&last_pixel_format, 0, sizeof(MDFN_PixelFormat));
|
||||
|
||||
@ -865,8 +800,6 @@ bool MDFNI_InitializeModules(const std::vector<MDFNGI *> &ExternalSystems)
|
||||
&EmulatedSMS,
|
||||
&EmulatedGG,
|
||||
#endif
|
||||
|
||||
&EmulatedCDPlay
|
||||
};
|
||||
std::string i_modules_string, e_modules_string;
|
||||
|
||||
@ -906,16 +839,6 @@ int MDFNI_Initialize(const char *basedir, const std::vector<MDFNSetting> &Driver
|
||||
// FIXME static
|
||||
static std::vector<MDFNSetting> dynamic_settings;
|
||||
|
||||
// DO NOT REMOVE/DISABLE THESE MATH AND COMPILER SANITY TESTS. THEY EXIST FOR A REASON.
|
||||
if(!MDFN_RunMathTests())
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
memset(PortDataCache, 0, sizeof(PortDataCache));
|
||||
memset(PortDataLenCache, 0, sizeof(PortDataLenCache));
|
||||
memset(PortDeviceCache, 0, sizeof(PortDeviceCache));
|
||||
|
||||
lzo_init();
|
||||
|
||||
MDFNI_SetBaseDirectory(basedir);
|
||||
@ -941,15 +864,6 @@ int MDFNI_Initialize(const char *basedir, const std::vector<MDFNSetting> &Driver
|
||||
|
||||
BuildDynamicSetting(&setting, sysname, "enable", MDFNSF_COMMON_TEMPLATE, CSD_enable, MDFNST_BOOL, "1");
|
||||
dynamic_settings.push_back(setting);
|
||||
|
||||
BuildDynamicSetting(&setting, sysname, "tblur", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_VIDEO, CSD_tblur, MDFNST_BOOL, "0");
|
||||
dynamic_settings.push_back(setting);
|
||||
|
||||
BuildDynamicSetting(&setting, sysname, "tblur.accum", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_VIDEO, CSD_tblur_accum, MDFNST_BOOL, "0");
|
||||
dynamic_settings.push_back(setting);
|
||||
|
||||
BuildDynamicSetting(&setting, sysname, "tblur.accum.amount", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_VIDEO, CSD_tblur_accum_amount, MDFNST_FLOAT, "50", "0", "100");
|
||||
dynamic_settings.push_back(setting);
|
||||
}
|
||||
|
||||
if(DriverSettings.size())
|
||||
@ -1003,31 +917,6 @@ static void ProcessAudio(EmulateSpecStruct *espec)
|
||||
const int32 SoundBufMaxSize = espec->SoundBufMaxSize - espec->SoundBufSizeALMS;
|
||||
|
||||
|
||||
if(espec->NeedSoundReverse)
|
||||
{
|
||||
int16 *yaybuf = SoundBuf;
|
||||
int32 slen = SoundBufSize;
|
||||
|
||||
if(MDFNGameInfo->soundchan == 1)
|
||||
{
|
||||
for(int x = 0; x < (slen / 2); x++)
|
||||
{
|
||||
int16 cha = yaybuf[slen - x - 1];
|
||||
yaybuf[slen - x - 1] = yaybuf[x];
|
||||
yaybuf[x] = cha;
|
||||
}
|
||||
}
|
||||
else if(MDFNGameInfo->soundchan == 2)
|
||||
{
|
||||
for(int x = 0; x < (slen * 2) / 2; x++)
|
||||
{
|
||||
int16 cha = yaybuf[slen * 2 - (x&~1) - ((x&1) ^ 1) - 1];
|
||||
yaybuf[slen * 2 - (x&~1) - ((x&1) ^ 1) - 1] = yaybuf[x];
|
||||
yaybuf[x] = cha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(multiplier_save != LastSoundMultiplier)
|
||||
{
|
||||
ff_resampler.time_ratio(multiplier_save, 0.9965);
|
||||
@ -1164,9 +1053,6 @@ void MDFNI_Emulate(EmulateSpecStruct *espec)
|
||||
ff_resampler.buffer_size((espec->SoundRate / 2) * 2);
|
||||
}
|
||||
|
||||
if(TBlur_IsOn())
|
||||
espec->skip = 0;
|
||||
|
||||
if(espec->NeedRewind)
|
||||
{
|
||||
if(MDFNGameInfo->GameType == GMT_PLAYER)
|
||||
@ -1179,7 +1065,7 @@ void MDFNI_Emulate(EmulateSpecStruct *espec)
|
||||
// Don't even save states with state rewinding if netplay is enabled, it will degrade netplay performance, and can cause
|
||||
// desynchs with some emulation(IE SNES based on bsnes).
|
||||
|
||||
espec->NeedSoundReverse = MDFN_StateEvil(espec->NeedRewind);
|
||||
espec->NeedSoundReverse = false;
|
||||
|
||||
MDFNGameInfo->Emulate(espec);
|
||||
|
||||
@ -1213,8 +1099,6 @@ void MDFNI_Emulate(EmulateSpecStruct *espec)
|
||||
PrevInterlaced = false;
|
||||
|
||||
ProcessAudio(espec);
|
||||
|
||||
TBlur_Run(espec);
|
||||
}
|
||||
|
||||
// This function should only be called for state rewinding.
|
||||
@ -1230,16 +1114,8 @@ int MDFN_RawInputStateAction(StateMem *sm, int load, int data_only)
|
||||
StateRegs[x].name = stringies[x];
|
||||
StateRegs[x].flags = 0;
|
||||
|
||||
if(PortDataCache[x])
|
||||
{
|
||||
StateRegs[x].v = PortDataCache[x];
|
||||
StateRegs[x].size = PortDataLenCache[x];
|
||||
}
|
||||
else
|
||||
{
|
||||
StateRegs[x].v = NULL;
|
||||
StateRegs[x].size = 0;
|
||||
}
|
||||
StateRegs[x].v = NULL;
|
||||
StateRegs[x].size = 0;
|
||||
}
|
||||
|
||||
StateRegs[x].v = NULL;
|
||||
@ -1427,17 +1303,6 @@ void MDFNI_SetInput(int port, const char *type, void *ptr, uint32 ptr_len_thingy
|
||||
{
|
||||
assert(port < 16);
|
||||
|
||||
PortDataCache[port] = ptr;
|
||||
PortDataLenCache[port] = ptr_len_thingy;
|
||||
|
||||
if(PortDeviceCache[port])
|
||||
{
|
||||
free(PortDeviceCache[port]);
|
||||
PortDeviceCache[port] = NULL;
|
||||
}
|
||||
|
||||
PortDeviceCache[port] = strdup(type);
|
||||
|
||||
MDFNGameInfo->SetInput(port, type, ptr);
|
||||
}
|
||||
}
|
||||
|
@ -24,44 +24,17 @@
|
||||
|
||||
void *MDFN_calloc_real(uint32 nmemb, uint32 size, const char *purpose, const char *_file, const int _line)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = calloc(nmemb, size);
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
MDFN_PrintError(_("Error allocating(calloc) %u bytes for \"%s\" in %s(%d)!"), size, purpose, _file, _line);
|
||||
return(0);
|
||||
}
|
||||
return ret;
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
void *MDFN_malloc_real(uint32 size, const char *purpose, const char *_file, const int _line)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = malloc(size);
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
MDFN_PrintError(_("Error allocating(malloc) %u bytes for \"%s\" in %s(%d)!"), size, purpose, _file, _line);
|
||||
return(0);
|
||||
}
|
||||
return ret;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *MDFN_realloc_real(void *ptr, uint32 size, const char *purpose, const char *_file, const int _line)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = realloc(ptr, size);
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
MDFN_PrintError(_("Error allocating(realloc) %u bytes for \"%s\" in %s(%d)!"), size, purpose, _file, _line);
|
||||
return(0);
|
||||
}
|
||||
return ret;
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void MDFN_free(void *ptr)
|
||||
|
@ -1,6 +0,0 @@
|
||||
|
||||
void MDFNI_SelectMovie(int);
|
||||
|
||||
void MDFNI_SaveMovie(char *fname, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const MDFN_Rect *LineWidths);
|
||||
void MDFNI_LoadMovie(char *fname);
|
||||
|
@ -1,353 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "mednafen.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <trio/trio.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "state.h"
|
||||
#include "general.h"
|
||||
#include "video.h"
|
||||
#include "netplay.h"
|
||||
#include "movie.h"
|
||||
|
||||
static int current = 0; // > 0 for recording, < 0 for playback
|
||||
static gzFile slots[10]={0};
|
||||
|
||||
static int CurrentMovie = 0;
|
||||
static int RecentlySavedMovie = -1;
|
||||
static int MovieStatus[10];
|
||||
static StateMem RewindBuffer;
|
||||
|
||||
bool MDFNMOV_IsPlaying(void)
|
||||
{
|
||||
if(current < 0) return(1);
|
||||
else return(0);
|
||||
}
|
||||
|
||||
bool MDFNMOV_IsRecording(void)
|
||||
{
|
||||
if(current > 0) return(1);
|
||||
else return(0);
|
||||
}
|
||||
|
||||
static void StopRecording(void)
|
||||
{
|
||||
MDFNMOV_RecordState();
|
||||
if(MDFN_StateEvilIsRunning())
|
||||
{
|
||||
MDFN_StateEvilFlushMovieLove();
|
||||
}
|
||||
gzclose(slots[current-1]);
|
||||
MovieStatus[current - 1] = 1;
|
||||
RecentlySavedMovie = current - 1;
|
||||
current=0;
|
||||
MDFN_DispMessage(_("Movie recording stopped."));
|
||||
|
||||
if(RewindBuffer.data)
|
||||
{
|
||||
//puts("Oops");
|
||||
free(RewindBuffer.data);
|
||||
RewindBuffer.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_SaveMovie(char *fname, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const MDFN_Rect *LineWidths)
|
||||
{
|
||||
gzFile fp;
|
||||
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
return;
|
||||
|
||||
if(MDFNnetplay && (MDFNGameInfo->SaveStateAltersState == true))
|
||||
{
|
||||
char sb[256];
|
||||
trio_snprintf(sb, sizeof(sb), _("Module %s is not compatible with manual movie save starting/stopping during netplay."), MDFNGameInfo->shortname);
|
||||
MDFND_NetplayText((const uint8*)sb, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if(current < 0) /* Can't interrupt playback.*/
|
||||
return;
|
||||
|
||||
if(current > 0) /* Stop saving. */
|
||||
{
|
||||
StopRecording();
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&RewindBuffer, 0, sizeof(StateMem));
|
||||
RewindBuffer.initial_malloc = 16;
|
||||
|
||||
current = CurrentMovie;
|
||||
|
||||
if(fname)
|
||||
fp = gzopen(fname, "wb3");
|
||||
else
|
||||
{
|
||||
fp=gzopen(MDFN_MakeFName(MDFNMKF_MOVIE,CurrentMovie,0).c_str(),"wb3");
|
||||
}
|
||||
|
||||
if(!fp) return;
|
||||
|
||||
MDFNSS_SaveFP(fp, surface, DisplayRect, LineWidths);
|
||||
gzseek(fp, 0, SEEK_END);
|
||||
gzflush(fp, Z_SYNC_FLUSH); // Flush output so that previews will still work right while
|
||||
// the movie is being recorded. Purely cosmetic. :)
|
||||
slots[current] = fp;
|
||||
current++;
|
||||
MDFN_DispMessage(_("Movie recording started."));
|
||||
}
|
||||
|
||||
static void StopPlayback(void)
|
||||
{
|
||||
if(RewindBuffer.data)
|
||||
{
|
||||
RewindBuffer.data = NULL;
|
||||
}
|
||||
|
||||
gzclose(slots[-1 - current]);
|
||||
current=0;
|
||||
MDFN_DispMessage(_("Movie playback stopped."));
|
||||
}
|
||||
|
||||
void MDFNMOV_Stop(void)
|
||||
{
|
||||
if(current < 0) StopPlayback();
|
||||
if(current > 0) StopRecording();
|
||||
}
|
||||
|
||||
void MDFNI_LoadMovie(char *fname)
|
||||
{
|
||||
gzFile fp;
|
||||
//puts("KAO");
|
||||
|
||||
if(current > 0) /* Can't interrupt recording.*/
|
||||
return;
|
||||
|
||||
if(MDFNnetplay) /* Playback is UNPOSSIBLE during netplay. */
|
||||
{
|
||||
MDFN_DispMessage(_("Can't play movies during netplay."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(current < 0) /* Stop playback. */
|
||||
{
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
|
||||
if(fname)
|
||||
fp = gzopen(fname, "rb");
|
||||
else
|
||||
{
|
||||
fp=gzopen(MDFN_MakeFName(MDFNMKF_MOVIE,CurrentMovie,0).c_str(),"rb");
|
||||
}
|
||||
|
||||
if(!fp) return;
|
||||
|
||||
if(!MDFNSS_LoadFP(fp))
|
||||
{
|
||||
MDFN_DispMessage(_("Error loading state portion of the movie."));
|
||||
return;
|
||||
}
|
||||
current = CurrentMovie;
|
||||
slots[current] = fp;
|
||||
|
||||
current = -1 - current;
|
||||
MovieStatus[CurrentMovie] = 1;
|
||||
|
||||
MDFN_DispMessage(_("Movie playback started."));
|
||||
}
|
||||
|
||||
// Donuts are a tasty treat and delicious with powdered sugar.
|
||||
void MDFNMOV_AddJoy(void *donutdata, uint32 donutlen)
|
||||
{
|
||||
gzFile fp;
|
||||
|
||||
if(!current) return; /* Not playback nor recording. */
|
||||
if(current < 0) /* Playback */
|
||||
{
|
||||
int t;
|
||||
|
||||
fp = slots[-1 - current];
|
||||
|
||||
while((t = gzgetc(fp)) >= 0 && t)
|
||||
{
|
||||
if(t == MDFNNPCMD_LOADSTATE)
|
||||
{
|
||||
uint32 len;
|
||||
StateMem sm;
|
||||
len = gzgetc(fp);
|
||||
len |= gzgetc(fp) << 8;
|
||||
len |= gzgetc(fp) << 16;
|
||||
len |= gzgetc(fp) << 24;
|
||||
if(len >= 5 * 1024 * 1024) // A sanity limit of 5MiB
|
||||
{
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
memset(&sm, 0, sizeof(StateMem));
|
||||
sm.len = len;
|
||||
sm.data = (uint8 *)malloc(len);
|
||||
if(gzread(fp, sm.data, len) != len)
|
||||
{
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
if(!MDFNSS_LoadSM(&sm, 0, 0))
|
||||
{
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
MDFN_DoSimpleCommand(t);
|
||||
}
|
||||
if(t < 0)
|
||||
{
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
|
||||
if(gzread(fp, donutdata, donutlen) != donutlen)
|
||||
{
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else /* Recording */
|
||||
{
|
||||
if(MDFN_StateEvilIsRunning())
|
||||
{
|
||||
smem_putc(&RewindBuffer, 0);
|
||||
smem_write(&RewindBuffer, donutdata, donutlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
fp = slots[current - 1];
|
||||
gzputc(fp, 0);
|
||||
gzwrite(fp, donutdata, donutlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNMOV_AddCommand(int cmd)
|
||||
{
|
||||
if(current <= 0) return; /* Return if not recording a movie */
|
||||
|
||||
if(MDFN_StateEvilIsRunning())
|
||||
smem_putc(&RewindBuffer, 0);
|
||||
else
|
||||
gzputc(slots[current - 1], cmd);
|
||||
}
|
||||
|
||||
void MDFNMOV_RecordState(void)
|
||||
{
|
||||
gzFile fp = slots[current - 1];
|
||||
StateMem sm;
|
||||
|
||||
memset(&sm, 0, sizeof(StateMem));
|
||||
MDFNSS_SaveSM(&sm, 0, 0);
|
||||
|
||||
if(MDFN_StateEvilIsRunning())
|
||||
{
|
||||
smem_putc(&RewindBuffer, MDFNNPCMD_LOADSTATE);
|
||||
smem_putc(&RewindBuffer, sm.len & 0xFF);
|
||||
smem_putc(&RewindBuffer, (sm.len >> 8) & 0xFF);
|
||||
smem_putc(&RewindBuffer, (sm.len >> 16) & 0xFF);
|
||||
smem_putc(&RewindBuffer, (sm.len >> 24) & 0xFF);
|
||||
smem_write(&RewindBuffer, sm.data, sm.len);
|
||||
}
|
||||
else
|
||||
{
|
||||
gzputc(fp, MDFNNPCMD_LOADSTATE);
|
||||
gzputc(fp, sm.len & 0xFF);
|
||||
gzputc(fp, (sm.len >> 8) & 0xFF);
|
||||
gzputc(fp, (sm.len >> 16) & 0xFF);
|
||||
gzputc(fp, (sm.len >> 24) & 0xFF);
|
||||
gzwrite(slots[current - 1], sm.data, sm.len);
|
||||
}
|
||||
free(sm.data);
|
||||
}
|
||||
|
||||
void MDFNMOV_ForceRecord(StateMem *sm)
|
||||
{
|
||||
//printf("Farced: %d\n", sm->len);
|
||||
gzwrite(slots[current - 1], sm->data, sm->len);
|
||||
}
|
||||
|
||||
StateMem MDFNMOV_GrabRewindJoy(void)
|
||||
{
|
||||
StateMem ret = RewindBuffer;
|
||||
memset(&RewindBuffer, 0, sizeof(StateMem));
|
||||
RewindBuffer.initial_malloc = 16;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void MDFNMOV_CheckMovies(void)
|
||||
{
|
||||
time_t last_time = 0;
|
||||
|
||||
for(int ssel = 0; ssel < 10; ssel++)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
||||
MovieStatus[ssel] = 0;
|
||||
if(stat(MDFN_MakeFName(MDFNMKF_MOVIE, ssel, 0).c_str(), &stat_buf) == 0)
|
||||
{
|
||||
MovieStatus[ssel] = 1;
|
||||
if(stat_buf.st_mtime > last_time)
|
||||
{
|
||||
RecentlySavedMovie = ssel;
|
||||
last_time = stat_buf.st_mtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
CurrentMovie = 0;
|
||||
}
|
||||
|
||||
void MDFNI_SelectMovie(int w)
|
||||
{
|
||||
StateStatusStruct *status = NULL;
|
||||
|
||||
if(w == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MDFNI_SelectState(-1);
|
||||
|
||||
CurrentMovie = w;
|
||||
MDFN_ResetMessages();
|
||||
|
||||
status = (StateStatusStruct*)MDFN_calloc(1, sizeof(StateStatusStruct), _("Movie status"));
|
||||
|
||||
memcpy(status->status, MovieStatus, 10 * sizeof(int));
|
||||
status->current = CurrentMovie;
|
||||
status->current_movie = current;
|
||||
status->recently_saved = RecentlySavedMovie;
|
||||
|
||||
MDFNSS_GetStateInfo(MDFN_MakeFName(MDFNMKF_MOVIE,CurrentMovie,NULL).c_str(), status);
|
||||
MDFND_SetMovieStatus(status);
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
#ifndef _MOVIE_H
|
||||
#define _MOVIE_H
|
||||
|
||||
void MDFNI_SelectMovie(int);
|
||||
|
||||
#include "movie-driver.h"
|
||||
#include "state.h"
|
||||
void MDFNMOV_AddJoy(void *donutdata, uint32 donutlen);
|
||||
void MDFNMOV_CheckMovies(void);
|
||||
void MDFNMOV_Stop(void);
|
||||
void MDFNMOV_AddCommand(int cmd);
|
||||
bool MDFNMOV_IsPlaying(void);
|
||||
bool MDFNMOV_IsRecording(void);
|
||||
void MDFNMOV_ForceRecord(StateMem *sm);
|
||||
StateMem MDFNMOV_GrabRewindJoy(void);
|
||||
void MDFNMOV_RecordState();
|
||||
#endif
|
@ -1,46 +0,0 @@
|
||||
/* Network interface */
|
||||
|
||||
void MDFNI_NetplayChangeNick(UTF8 *newnick);
|
||||
|
||||
/* Call when network play needs to stop. */
|
||||
void MDFNI_NetplayStop(void);
|
||||
|
||||
/* Note: YOU MUST NOT CALL ANY MDFNI_* FUNCTIONS WHILE IN MDFND_SendData() or
|
||||
MDFND_RecvData().
|
||||
*/
|
||||
|
||||
void MDFND_SendData(const void *data, uint32 len); // thrown std::exception will be handled
|
||||
void MDFND_RecvData(void *data, uint32 len); // thrown std::exception will be handled
|
||||
|
||||
/* Display text received over the network. */
|
||||
/* NetEcho will be set to TRUE if the displayed text is a network
|
||||
echo of what we typed.
|
||||
*/
|
||||
void MDFND_NetplayText(const uint8 *text, bool NetEcho);
|
||||
|
||||
/* Encode and send text over the network. */
|
||||
void MDFNI_NetplayText(const uint8 *text);
|
||||
|
||||
|
||||
void MDFNI_NetplayQuit(const char *quit_message);
|
||||
void MDFNI_NetplaySwap(uint8 a, uint8 b);
|
||||
void MDFNI_NetplayTake(uint32 mask);
|
||||
void MDFNI_NetplayDrop(uint32 mask);
|
||||
void MDFNI_NetplayDupe(uint32 mask);
|
||||
|
||||
void MDFNI_NetplayList(void);
|
||||
|
||||
/* Starts a process to determine the integrity(synchronization) of all players
|
||||
in the current game session on the server. Outputs result via MDFND_NetplayText()
|
||||
when they are received from the server(not in this function).
|
||||
*/
|
||||
void MDFNI_NetplayIntegrity(void);
|
||||
|
||||
/* Pings the server. Outputs result via MDFND_NetplayText() when received from the server(not in this function). */
|
||||
void MDFNI_NetplayPing(void);
|
||||
|
||||
/* Called when a fatal error occurred and network play can't continue. This function
|
||||
should call MDFNI_NetplayStop() after it has deinitialized the network on the driver
|
||||
side.
|
||||
*/
|
||||
void MDFND_NetworkClose(void);
|
@ -1,846 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// Protocol versions:
|
||||
// 1 - I forgot!
|
||||
// 2 - Added support for more versatile input configurations...somewhat
|
||||
// 3 -
|
||||
|
||||
#include "mednafen.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
#include <trio/trio.h>
|
||||
|
||||
#include "netplay.h"
|
||||
#include "netplay-driver.h"
|
||||
#include "general.h"
|
||||
#include "state.h"
|
||||
#include "movie.h"
|
||||
#include "md5.h"
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
int MDFNnetplay=0;
|
||||
|
||||
static char *OurNick = NULL;
|
||||
|
||||
static bool Joined = 0;
|
||||
static uint32 LocalPlayersMask = 0;
|
||||
static uint32 TotalInputStateSize = 0;
|
||||
static uint32 LocalInputStateSize = 0;
|
||||
|
||||
static void NetError(const char *format, ...)
|
||||
{
|
||||
char *temp = NULL;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
temp = trio_vaprintf(format, ap);
|
||||
va_end(ap);
|
||||
|
||||
MDFND_NetplayText((UTF8 *)temp, FALSE);
|
||||
MDFND_NetworkClose();
|
||||
free(temp);
|
||||
}
|
||||
|
||||
void MDFNI_NetplayStop(void)
|
||||
{
|
||||
if(MDFNnetplay)
|
||||
{
|
||||
Joined = false;
|
||||
MDFNnetplay = 0;
|
||||
MDFN_FlushGameCheats(1); /* Don't save netplay cheats. */
|
||||
MDFN_LoadGameCheats(0); /* Reload our original cheats. */
|
||||
if(OurNick)
|
||||
{
|
||||
free(OurNick);
|
||||
OurNick = NULL;
|
||||
}
|
||||
}
|
||||
else puts("Check your code!");
|
||||
}
|
||||
|
||||
int NetplayStart(const char *PortDeviceCache[16], const uint32 PortDataLenCache[16], uint32 local_players, const std::string &nickname, const std::string &game_key, const std::string &connect_password)
|
||||
{
|
||||
uint8 *sendbuf;
|
||||
uint32 sblen;
|
||||
const char *emu_id = PACKAGE " " MEDNAFEN_VERSION;
|
||||
|
||||
try
|
||||
{
|
||||
sblen = 4 + 16 + 16 + 64 + 1 + nickname.size() + strlen(emu_id);
|
||||
sendbuf = (uint8 *)malloc(sblen);
|
||||
memset(sendbuf, 0, sblen);
|
||||
|
||||
MDFN_en32lsb(sendbuf, sblen - 4);
|
||||
if(game_key != "")
|
||||
{
|
||||
md5_context md5;
|
||||
uint8 md5out[16];
|
||||
|
||||
md5.starts();
|
||||
md5.update(MDFNGameInfo->MD5, 16);
|
||||
md5.update((uint8 *)game_key.c_str(), game_key.size());
|
||||
md5.finish(md5out);
|
||||
memcpy(sendbuf + 4, md5out, 16);
|
||||
}
|
||||
else
|
||||
memcpy(sendbuf + 4, MDFNGameInfo->MD5, 16);
|
||||
|
||||
if(connect_password != "")
|
||||
{
|
||||
md5_context md5;
|
||||
uint8 md5out[16];
|
||||
|
||||
md5.starts();
|
||||
md5.update((uint8*)connect_password.c_str(), connect_password.size());
|
||||
md5.finish(md5out);
|
||||
memcpy(sendbuf + 4 + 16, md5out, 16);
|
||||
}
|
||||
|
||||
uint8 *extra = sendbuf + 4 + 16 + 16;
|
||||
|
||||
extra[0] = 3; // Protocol version
|
||||
|
||||
// Set input device number thingies here.
|
||||
extra[1] = MDFNGameInfo->InputInfo->InputPorts; // Total number of ports
|
||||
|
||||
// The size of the giganto string with the controller types
|
||||
MDFN_en32lsb(&extra[4], strlen(emu_id));
|
||||
|
||||
// 16-32, controller data sizes
|
||||
for(int x = 0; x < MDFNGameInfo->InputInfo->InputPorts; x++)
|
||||
extra[16 + x] = PortDataLenCache[x];
|
||||
|
||||
|
||||
sendbuf[4 + 16 + 16 + 64] = local_players;
|
||||
|
||||
if(nickname != "")
|
||||
memcpy(sendbuf + 4 + 16 + 16 + 64 + 1, nickname.c_str(), nickname.size());
|
||||
|
||||
memcpy(sendbuf + 4 + 16 + 16 + 64 + 1 + nickname.size(), emu_id, strlen(emu_id));
|
||||
|
||||
MDFND_SendData(sendbuf, sblen);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
return(false);
|
||||
}
|
||||
|
||||
TotalInputStateSize = 0;
|
||||
for(int x = 0; x < MDFNGameInfo->InputInfo->InputPorts; x++)
|
||||
TotalInputStateSize += PortDataLenCache[x];
|
||||
|
||||
// Hack so the server can always encode its command data length properly(a matching "hack" exists in the server).
|
||||
if(TotalInputStateSize < 4)
|
||||
TotalInputStateSize = 4;
|
||||
|
||||
LocalPlayersMask = 0;
|
||||
LocalInputStateSize = 0;
|
||||
Joined = 0;
|
||||
|
||||
MDFN_FlushGameCheats(0); /* Save our pre-netplay cheats. */
|
||||
MDFNnetplay = 1;
|
||||
|
||||
if(MDFNMOV_IsPlaying()) /* Recording's ok during netplay, playback is not. */
|
||||
MDFNMOV_Stop();
|
||||
|
||||
//printf("%d\n", TotalInputStateSize);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static void SendCommand(uint8 cmd, uint32 len)
|
||||
{
|
||||
uint8 buf[1 + LocalInputStateSize + 4]; // Command, unused, command length
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
buf[0] = cmd;
|
||||
MDFN_en32lsb(&buf[1 + LocalInputStateSize], len);
|
||||
MDFND_SendData(buf,LocalInputStateSize + 1 + 4);
|
||||
}
|
||||
|
||||
bool NetplaySendCommand(uint8 cmd, uint32 len)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(cmd, len);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void MDFNI_NetplaySwap(uint8 a, uint8 b)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_CTRLR_SWAP, (a << 0) | (b << 8));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayTake(uint32 mask)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_CTRLR_TAKE, mask);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayDrop(uint32 mask)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_CTRLR_DROP, mask);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayDupe(uint32 mask)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_CTRLR_DUPE, mask);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayList(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_REQUEST_LIST, 0);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MDFNI_NetplayPing(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
uint64 now_time;
|
||||
|
||||
now_time = MDFND_GetTime();
|
||||
|
||||
SendCommand(MDFNNPCMD_ECHO, sizeof(now_time));
|
||||
|
||||
// Endianness doesn't matter, since it will be echoed back only to us.
|
||||
MDFND_SendData(&now_time, sizeof(now_time));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayIntegrity(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_INTEGRITY, 0);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayText(const uint8 *text)
|
||||
{
|
||||
try
|
||||
{
|
||||
uint32 len;
|
||||
|
||||
if(!Joined) return;
|
||||
|
||||
len = strlen((char *)text);
|
||||
|
||||
SendCommand(MDFNNPCMD_TEXT, len);
|
||||
|
||||
MDFND_SendData(text,len);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayChangeNick(UTF8 *newnick)
|
||||
{
|
||||
try
|
||||
{
|
||||
uint32 len;
|
||||
|
||||
if(!Joined) return;
|
||||
|
||||
len = strlen((char *)newnick);
|
||||
|
||||
SendCommand(MDFNNPCMD_SETNICK, len);
|
||||
|
||||
MDFND_SendData(newnick, len);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNI_NetplayQuit(const char *quit_message)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendCommand(MDFNNPCMD_QUIT, strlen(quit_message));
|
||||
MDFND_SendData(quit_message, strlen(quit_message));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Integrity checking is experimental, and needs work to function properly(in the emulator cores).
|
||||
static int SendIntegrity(void)
|
||||
{
|
||||
StateMem sm;
|
||||
md5_context md5;
|
||||
uint8 digest[16];
|
||||
|
||||
memset(&sm, 0, sizeof(StateMem));
|
||||
|
||||
// Do not do a raw/data-only state for speed, due to lack of endian and bool conversion.
|
||||
if(!MDFNSS_SaveSM(&sm, 0, false))
|
||||
{
|
||||
throw MDFN_Error(0, _("Error during save state generation."));
|
||||
}
|
||||
|
||||
md5.starts();
|
||||
md5.update(sm.data, sm.len);
|
||||
md5.finish(digest);
|
||||
|
||||
free(sm.data);
|
||||
|
||||
//for(int i = 15; i >= 0; i--)
|
||||
// printf("%02x", digest[i]);
|
||||
//puts("");
|
||||
|
||||
SendCommand(MDFNNPCMD_INTEGRITY_RES, 16);
|
||||
MDFND_SendData(digest, 16);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static void SendState(void)
|
||||
{
|
||||
StateMem sm;
|
||||
uLongf clen;
|
||||
std::vector<uint8> cbuf;
|
||||
|
||||
memset(&sm, 0, sizeof(StateMem));
|
||||
|
||||
if(!MDFNSS_SaveSM(&sm, 0, 0))
|
||||
{
|
||||
throw MDFN_Error(0, _("Error during save state generation."));
|
||||
}
|
||||
|
||||
clen = sm.len + sm.len / 1000 + 12;
|
||||
cbuf.resize(4 + clen);
|
||||
MDFN_en32lsb(&cbuf[0], sm.len);
|
||||
compress2((Bytef *)&cbuf[0] + 4, &clen, (Bytef *)sm.data, sm.len, 7);
|
||||
free(sm.data);
|
||||
|
||||
SendCommand(MDFNNPCMD_LOADSTATE, clen + 4);
|
||||
MDFND_SendData(&cbuf[0], clen + 4);
|
||||
}
|
||||
|
||||
static void RecvState(const uint32 clen)
|
||||
{
|
||||
StateMem sm;
|
||||
std::vector<uint8> cbuf;
|
||||
std::vector<uint8> buf;
|
||||
|
||||
memset(&sm, 0, sizeof(StateMem));
|
||||
|
||||
if(clen < 4)
|
||||
{
|
||||
throw MDFN_Error(0, _("Compressed save state data is too small: %u"), clen);
|
||||
}
|
||||
|
||||
if(clen > 8 * 1024 * 1024) // Compressed length sanity check - 8 MiB max.
|
||||
{
|
||||
throw MDFN_Error(0, _("Compressed save state data is too large: %u"), clen);
|
||||
}
|
||||
|
||||
cbuf.resize(clen);
|
||||
|
||||
MDFND_RecvData(&cbuf[0], clen);
|
||||
|
||||
uLongf len = MDFN_de32lsb(&cbuf[0]);
|
||||
if(len > 12 * 1024 * 1024) // Uncompressed length sanity check - 12 MiB max.
|
||||
{
|
||||
throw MDFN_Error(0, _("Uncompressed save state data is too large: %u"), len);
|
||||
}
|
||||
|
||||
buf.resize(len);
|
||||
|
||||
uncompress((Bytef *)&buf[0], &len, (Bytef *)&cbuf[0] + 4, clen - 4);
|
||||
|
||||
sm.data = &buf[0];
|
||||
sm.len = len;
|
||||
|
||||
if(!MDFNSS_LoadSM(&sm, 0, 0))
|
||||
{
|
||||
throw MDFN_Error(0, _("Error during save state loading."));
|
||||
}
|
||||
|
||||
if(MDFNMOV_IsRecording())
|
||||
MDFNMOV_RecordState();
|
||||
}
|
||||
|
||||
static std::string GenerateMPSString(uint32 mps, bool ctlr_string = false)
|
||||
{
|
||||
char tmpbuf[256];
|
||||
|
||||
tmpbuf[0] = 0;
|
||||
|
||||
if(!mps)
|
||||
{
|
||||
if(!ctlr_string)
|
||||
trio_snprintf(tmpbuf, sizeof(tmpbuf), _("a lurker"));
|
||||
}
|
||||
else
|
||||
trio_snprintf(tmpbuf, sizeof(tmpbuf), ("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"), ctlr_string ? ((mps == round_up_pow2(mps)) ? _("controller") : _("controllers")) : ((mps == round_up_pow2(mps)) ? _("player") : _("players")),
|
||||
(mps & 0x0001) ? " 1" : "",
|
||||
(mps & 0x0002) ? " 2" : "",
|
||||
(mps & 0x0004) ? " 3" : "",
|
||||
(mps & 0x0008) ? " 4" : "",
|
||||
(mps & 0x0010) ? " 5" : "",
|
||||
(mps & 0x0020) ? " 6" : "",
|
||||
(mps & 0x0040) ? " 7" : "",
|
||||
(mps & 0x0080) ? " 8" : "",
|
||||
(mps & 0x0100) ? " 9" : "",
|
||||
(mps & 0x0200) ? " 10" : "",
|
||||
(mps & 0x0400) ? " 11" : "",
|
||||
(mps & 0x0800) ? " 12" : "",
|
||||
(mps & 0x1000) ? " 13" : "",
|
||||
(mps & 0x2000) ? " 14" : "",
|
||||
(mps & 0x4000) ? " 15" : "",
|
||||
(mps & 0x8000) ? " 16" : "");
|
||||
|
||||
|
||||
return(std::string(tmpbuf));
|
||||
}
|
||||
|
||||
void NetplaySendState(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendState();
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void NetplayUpdate(const char **PortDNames, void *PortData[], uint32 PortLen[], int NumPorts)
|
||||
{
|
||||
uint8 buf[TotalInputStateSize + 1];
|
||||
|
||||
//
|
||||
//
|
||||
try
|
||||
{
|
||||
if(Joined)
|
||||
{
|
||||
uint8 outgoing_buffer[1 + LocalInputStateSize];
|
||||
bool Taken[NumPorts];
|
||||
|
||||
memset(Taken, 0, sizeof(Taken));
|
||||
|
||||
outgoing_buffer[0] = 0; // This is not a command, duh!
|
||||
|
||||
int wpos = 1;
|
||||
|
||||
for(int x = 0; x < NumPorts; x++)
|
||||
{
|
||||
if(LocalPlayersMask & (1 << x))
|
||||
{
|
||||
|
||||
for(int n = 0; n <= x; n++)
|
||||
{
|
||||
if(!Taken[n] && !strcmp(PortDNames[n], PortDNames[x]))
|
||||
{
|
||||
memcpy(outgoing_buffer + wpos, PortData[n], PortLen[n]);
|
||||
Taken[n] = TRUE;
|
||||
wpos += PortLen[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
MDFND_SendData(outgoing_buffer, 1 + LocalInputStateSize);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
MDFND_RecvData(buf, TotalInputStateSize + 1);
|
||||
|
||||
switch(buf[TotalInputStateSize])
|
||||
{
|
||||
case 0: break; // No command
|
||||
|
||||
default: MDFN_DoSimpleCommand(buf[TotalInputStateSize]);break;
|
||||
|
||||
case MDFNNPCMD_INTEGRITY:
|
||||
SendIntegrity();
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_REQUEST_STATE:
|
||||
SendState();
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_LOADSTATE:
|
||||
RecvState(MDFN_de32lsb(buf));
|
||||
MDFN_DispMessage(_("Remote state loaded."));
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_SERVERTEXT:
|
||||
{
|
||||
static const uint32 MaxLength = 2000;
|
||||
uint8 neobuf[MaxLength + 1];
|
||||
char *textbuf = NULL;
|
||||
const uint32 totallen = MDFN_de32lsb(buf);
|
||||
|
||||
if(totallen > MaxLength) // Sanity check
|
||||
{
|
||||
throw MDFN_Error(0, _("Text length is too long: %u"), totallen);
|
||||
}
|
||||
|
||||
MDFND_RecvData(neobuf, totallen);
|
||||
|
||||
neobuf[totallen] = 0;
|
||||
trio_asprintf(&textbuf, "** %s", neobuf);
|
||||
MDFND_NetplayText((UTF8*)textbuf, FALSE);
|
||||
free(textbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_ECHO:
|
||||
{
|
||||
uint32 totallen = MDFN_de32lsb(buf);
|
||||
uint64 then_time;
|
||||
uint64 now_time;
|
||||
|
||||
if(totallen != sizeof(then_time))
|
||||
{
|
||||
throw MDFN_Error(0, _("Echo response length is incorrect size: %u"), totallen);
|
||||
}
|
||||
|
||||
MDFND_RecvData(&then_time, sizeof(then_time));
|
||||
|
||||
now_time = MDFND_GetTime();
|
||||
|
||||
char *textbuf = NULL;
|
||||
trio_asprintf(&textbuf, "*** Round-trip time: %llu ms", now_time - then_time);
|
||||
MDFND_NetplayText((UTF8*)textbuf, FALSE);
|
||||
free(textbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_TEXT:
|
||||
{
|
||||
static const uint32 MaxLength = 2000;
|
||||
uint8 neobuf[MaxLength + 1];
|
||||
const uint32 totallen = MDFN_de32lsb(buf);
|
||||
uint32 nicklen;
|
||||
bool NetEcho = false;
|
||||
char *textbuf = NULL;
|
||||
|
||||
if(totallen < 4)
|
||||
{
|
||||
throw MDFN_Error(0, _("Text command length is too short: %u"), totallen);
|
||||
}
|
||||
|
||||
if(totallen > MaxLength) // Sanity check
|
||||
{
|
||||
throw MDFN_Error(0, _("Text command length is too long: %u"), totallen);
|
||||
}
|
||||
|
||||
MDFND_RecvData(neobuf, totallen);
|
||||
|
||||
nicklen = MDFN_de32lsb(neobuf);
|
||||
if(nicklen > (totallen - 4)) // Sanity check
|
||||
{
|
||||
throw MDFN_Error(0, _("Received nickname length is too long: %u"), nicklen);
|
||||
}
|
||||
|
||||
neobuf[totallen] = 0;
|
||||
|
||||
if(nicklen)
|
||||
{
|
||||
uint8 nickbuf[nicklen + 1];
|
||||
memcpy(nickbuf, neobuf + 4, nicklen);
|
||||
nickbuf[nicklen] = 0;
|
||||
if(OurNick && !strcasecmp(OurNick, (char *)nickbuf))
|
||||
{
|
||||
trio_asprintf(&textbuf, "> %s", &neobuf[4 + nicklen]);
|
||||
NetEcho = true;
|
||||
}
|
||||
else
|
||||
trio_asprintf(&textbuf, "<%s> %s", nickbuf, &neobuf[4 + nicklen]);
|
||||
}
|
||||
else
|
||||
{
|
||||
trio_asprintf(&textbuf, "* %s", &neobuf[4]);
|
||||
}
|
||||
MDFND_NetplayText((UTF8*)textbuf, NetEcho);
|
||||
free(textbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_NICKCHANGED:
|
||||
{
|
||||
static const uint32 MaxLength = 2000;
|
||||
uint8 neobuf[MaxLength + 1];
|
||||
uint8 *newnick;
|
||||
char *textbuf = NULL;
|
||||
const uint32 len = MDFN_de32lsb(buf);
|
||||
|
||||
if(len > MaxLength) // Sanity check
|
||||
{
|
||||
throw MDFN_Error(0, _("Nickname change length is too long: %u"), len);
|
||||
}
|
||||
|
||||
MDFND_RecvData(neobuf, len);
|
||||
|
||||
neobuf[len] = 0;
|
||||
|
||||
newnick = (uint8*)strchr((char*)neobuf, '\n');
|
||||
|
||||
if(newnick)
|
||||
{
|
||||
bool IsMeow = FALSE;
|
||||
*newnick = 0;
|
||||
newnick++;
|
||||
if(OurNick)
|
||||
{
|
||||
if(!strcasecmp((char*)neobuf, (char*)OurNick))
|
||||
{
|
||||
free(OurNick);
|
||||
OurNick = strdup((char*)newnick);
|
||||
textbuf = trio_aprintf(_("* You are now known as <%s>."), newnick);
|
||||
IsMeow = TRUE;
|
||||
}
|
||||
}
|
||||
if(!textbuf)
|
||||
textbuf = trio_aprintf(_("* <%s> is now known as <%s>"), neobuf, newnick);
|
||||
MDFND_NetplayText((UTF8*)textbuf, IsMeow);
|
||||
free(textbuf);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_CTRL_CHANGE:
|
||||
{
|
||||
const uint32 len = MDFN_de32lsb(buf);
|
||||
|
||||
//
|
||||
// Joined = true;
|
||||
SendCommand(MDFNNPCMD_CTRL_CHANGE_ACK, len);
|
||||
//
|
||||
//
|
||||
LocalInputStateSize = 0;
|
||||
LocalPlayersMask = len;
|
||||
|
||||
for(int x = 0; x < MDFNGameInfo->InputInfo->InputPorts; x++)
|
||||
{
|
||||
if(LocalPlayersMask & (1 << x))
|
||||
LocalInputStateSize += PortLen[x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_CTRLR_SWAP_NOTIF:
|
||||
{
|
||||
const uint32 cm = MDFN_de32lsb(buf);
|
||||
char textbuf[512];
|
||||
|
||||
trio_snprintf(textbuf, sizeof(textbuf), _("* All instances of controllers %u and %u have been swapped."), ((cm & 0xFF) + 1), ((cm >> 8) & 0xFF) + 1);
|
||||
MDFND_NetplayText((UTF8*)textbuf, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_CTRLR_TAKE_NOTIF:
|
||||
case MDFNNPCMD_CTRLR_DROP_NOTIF:
|
||||
case MDFNNPCMD_CTRLR_DUPE_NOTIF:
|
||||
{
|
||||
static const uint32 MaxNicknameLength = 1000;
|
||||
static const uint32 MaxLength = 12 + MaxNicknameLength;
|
||||
const char *fstr = NULL;
|
||||
const uint32 len = MDFN_de32lsb(buf);
|
||||
uint8 ntf_buf[MaxLength + 1];
|
||||
char *textbuf = NULL;
|
||||
|
||||
if(len < 12)
|
||||
throw MDFN_Error(0, _("Take/drop/dupe notification is too short: %u"), len);
|
||||
|
||||
if(len > MaxLength)
|
||||
throw MDFN_Error(0, _("Take/drop/dupe notification is too long: %u"), len);
|
||||
|
||||
MDFND_RecvData(ntf_buf, len);
|
||||
ntf_buf[len] = 0;
|
||||
|
||||
switch(buf[TotalInputStateSize])
|
||||
{
|
||||
case MDFNNPCMD_CTRLR_TAKE_NOTIF:
|
||||
fstr = _("* <%s> took all instances of %s, and is now %s.");
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_CTRLR_DUPE_NOTIF:
|
||||
fstr = _("* <%s> took copies of %s, and is now %s.");
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_CTRLR_DROP_NOTIF:
|
||||
fstr = _("* <%s> dropped %s, and is now %s.");
|
||||
break;
|
||||
}
|
||||
trio_asprintf(&textbuf, fstr, ntf_buf + 12, GenerateMPSString(MDFN_de32lsb(&ntf_buf[0]), true).c_str(), GenerateMPSString(MDFN_de32lsb(&ntf_buf[4]), false).c_str());
|
||||
MDFND_NetplayText((UTF8*)textbuf, false);
|
||||
free(textbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case MDFNNPCMD_YOUJOINED:
|
||||
case MDFNNPCMD_YOULEFT:
|
||||
case MDFNNPCMD_PLAYERLEFT:
|
||||
case MDFNNPCMD_PLAYERJOINED:
|
||||
{
|
||||
static const uint32 MaxLength = 2000;
|
||||
uint8 neobuf[MaxLength + 1];
|
||||
char *textbuf = NULL;
|
||||
uint32 mps;
|
||||
std::string mps_string;
|
||||
const uint32 len = MDFN_de32lsb(buf);
|
||||
|
||||
if(len < 8)
|
||||
{
|
||||
throw MDFN_Error(0, _("Join/Left length is too short: %u"), len);
|
||||
}
|
||||
|
||||
if(len > MaxLength) // Sanity check
|
||||
{
|
||||
throw MDFN_Error(0, _("Join/Left length is too long: %u"), len);
|
||||
}
|
||||
|
||||
MDFND_RecvData(neobuf, len);
|
||||
neobuf[len] = 0; // NULL-terminate the string
|
||||
|
||||
mps = MDFN_de32lsb(&neobuf[0]);
|
||||
|
||||
mps_string = GenerateMPSString(mps);
|
||||
|
||||
if(buf[TotalInputStateSize] == MDFNNPCMD_YOULEFT)
|
||||
{
|
||||
// Uhm, not supported yet!
|
||||
LocalPlayersMask = 0;
|
||||
LocalInputStateSize = 0;
|
||||
Joined = FALSE;
|
||||
}
|
||||
else if(buf[TotalInputStateSize] == MDFNNPCMD_YOUJOINED)
|
||||
{
|
||||
if(OurNick) // This shouldn't happen, really...
|
||||
{
|
||||
free(OurNick);
|
||||
OurNick = NULL;
|
||||
}
|
||||
OurNick = strdup((char*)neobuf + 8);
|
||||
|
||||
trio_asprintf(&textbuf, _("* You, %s, have connected as: %s"), neobuf + 8, mps_string.c_str());
|
||||
|
||||
LocalInputStateSize = 0;
|
||||
LocalPlayersMask = mps;
|
||||
|
||||
for(int x = 0; x < MDFNGameInfo->InputInfo->InputPorts; x++)
|
||||
{
|
||||
if(LocalPlayersMask & (1 << x))
|
||||
LocalInputStateSize += PortLen[x];
|
||||
}
|
||||
Joined = TRUE;
|
||||
SendCommand(MDFNNPCMD_SETFPS, MDFNGameInfo->fps);
|
||||
}
|
||||
else if(buf[TotalInputStateSize] == MDFNNPCMD_PLAYERLEFT)
|
||||
{
|
||||
trio_asprintf(&textbuf, _("* %s(%s) has left"), neobuf + 8, mps_string.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
trio_asprintf(&textbuf, _("* %s has connected as: %s"), neobuf + 8, mps_string.c_str());
|
||||
}
|
||||
MDFND_NetplayText((UTF8*)textbuf, FALSE);
|
||||
free(textbuf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while(buf[TotalInputStateSize]);
|
||||
|
||||
int rpos = 0;
|
||||
for(int x = 0; x < NumPorts; x++)
|
||||
{
|
||||
//printf("%d %d\n", x, PortLen[x]);
|
||||
//memset(PortData[x], 0, PortLen[x]);
|
||||
memcpy(PortData[x], buf + rpos, PortLen[x]);
|
||||
rpos += PortLen[x];
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
NetError("%s", e.what());
|
||||
}
|
||||
//
|
||||
//
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
#ifndef _MDFN_NETPLAY_H
|
||||
#define _MDFN_NETPLAY_H
|
||||
|
||||
int InitNetplay(void);
|
||||
|
||||
void NetplayUpdate(const char **, void *PortData[], uint32 PortLen[], int NumPorts);
|
||||
int NetplayStart(const char *PortDeviceCache[16], const uint32 PortDataLenCache[16], uint32 local_players, const std::string &nickname, const std::string &game_key, const std::string &connect_password);
|
||||
void NetplaySendState(void);
|
||||
bool NetplaySendCommand(uint8, uint32);
|
||||
|
||||
extern int MDFNnetplay;
|
||||
|
||||
#define MDFNNPCMD_RESET MDFN_MSC_RESET
|
||||
#define MDFNNPCMD_POWER MDFN_MSC_POWER
|
||||
|
||||
#define MDFNNPCMD_VSUNICOIN MDFN_MSC_INSERT_COIN
|
||||
#define MDFNNPCMD_VSUNIDIP0 MDFN_MSC_TOGGLE_DIP0
|
||||
#define MDFNNPCMD_FDSINSERTx MDFN_MSC_INSERT_DISK0
|
||||
#define MDFNNPCMD_FDSINSERT MDFN_MSC_INSERT_DISK
|
||||
#define MDFNNPCMD_FDSEJECT MDFN_MSC_EJECT_DISK
|
||||
#define MDFNNPCMD_FDSSELECT MDFN_MSC_SELECT_DISK
|
||||
|
||||
#define MDFNNPCMD_SETFPS 0x40 // Client->server. It should be ignored server-side if it's not from the first
|
||||
// active player for the game).
|
||||
|
||||
#define MDFNNPCMD_NOP 0x41 // Client->server.
|
||||
|
||||
//
|
||||
#define MDFNNPCMD_CTRL_CHANGE 0x43 // Server->client.
|
||||
#define MDFNNPCMD_CTRL_CHANGE_ACK 0x44 // Client->server. Acknowledge controller change. Sent using old local data length, everything after
|
||||
// this should be new data size.
|
||||
//
|
||||
|
||||
#define MDFNNPCMD_CTRLR_SWAP_NOTIF 0x68 // Server->Client
|
||||
|
||||
#define MDFNNPCMD_CTRLR_TAKE 0x70 // Client->server. Take the specified controllers(from other clients)
|
||||
#define MDFNNPCMD_CTRLR_DROP 0x71 // Client->server. Drop(relinquish) the specified controllers.
|
||||
#define MDFNNPCMD_CTRLR_DUPE 0x72 // Client->server. Take the specified controllers(but let other clients still keep their control).
|
||||
|
||||
|
||||
#define MDFNNPCMD_CTRLR_SWAP 0x78 // Client->server.
|
||||
|
||||
#define MDFNNPCMD_REQUEST_LIST 0x7F // client->server
|
||||
|
||||
#define MDFNNPCMD_LOADSTATE 0x80 // Client->server, and server->client
|
||||
#define MDFNNPCMD_REQUEST_STATE 0x81 // Server->client
|
||||
|
||||
#define MDFNNPCMD_TEXT 0x90
|
||||
|
||||
#define MDFNNPCMD_SERVERTEXT 0x93 // Server text message(informational), server->client
|
||||
#define MDFNNPCMD_ECHO 0x94 // Echos the string(no larger than 256 bytes) back to the client(used for pinging).
|
||||
|
||||
#define MDFNNPCMD_INTEGRITY 0x95 // Send from a client to a server, then from the server to all clients.
|
||||
#define MDFNNPCMD_INTEGRITY_RES 0x96 // Integrity result, sent from the clients to the server. The result should be no larger
|
||||
// than 256 bytes.
|
||||
|
||||
#define MDFNNPCMD_SETNICK 0x98 /* Sent from client to server only. */
|
||||
|
||||
#define MDFNNPCMD_PLAYERJOINED 0xA0 // Data: <byte: bitmask, which inputs this player controls>
|
||||
// <bytestream: nickname>
|
||||
#define MDFNNPCMD_PLAYERLEFT 0xA1 // Data: (see above)
|
||||
|
||||
#define MDFNNPCMD_YOUJOINED 0xB0
|
||||
#define MDFNNPCMD_YOULEFT 0xB1
|
||||
|
||||
#define MDFNNPCMD_NICKCHANGED 0xB8
|
||||
|
||||
#define MDFNNPCMD_LIST 0xC0 // Server->client
|
||||
|
||||
#define MDFNNPCMD_CTRLR_TAKE_NOTIF 0xF0 // Server->client
|
||||
#define MDFNNPCMD_CTRLR_DROP_NOTIF 0xF1 // Server->client
|
||||
#define MDFNNPCMD_CTRLR_DUPE_NOTIF 0xF2 // Server->client
|
||||
|
||||
|
||||
#define MDFNNPCMD_QUIT 0xFF // Client->server
|
||||
|
||||
int MDFNNET_SendCommand(uint8, uint32);
|
||||
#endif
|
@ -1,224 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "mednafen.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "video.h"
|
||||
#include "player.h"
|
||||
|
||||
static std::string AlbumName, Artist, Copyright;
|
||||
static std::vector<std::string> SongNames;
|
||||
static int TotalSongs;
|
||||
|
||||
static INLINE void DrawLine(MDFN_Surface *surface, uint32 color, uint32 bmatch, uint32 breplace, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
uint32 *buf = surface->pixels;
|
||||
float dy_dx = (float)(y2 - y1) / (x2 - x1);
|
||||
int x;
|
||||
|
||||
float r_ys = 0; //x1 * dy_dx;
|
||||
float r_ye = (x2 - x1) * dy_dx;
|
||||
|
||||
for(x = x1; x <= x2; x++)
|
||||
{
|
||||
float ys = dy_dx * (x - 1 - x1) + dy_dx / 2;
|
||||
float ye = dy_dx * (x + 1 - x1) - dy_dx / 2;
|
||||
|
||||
if(dy_dx > 0)
|
||||
{
|
||||
ys = round(ys);
|
||||
ye = round(ye);
|
||||
|
||||
if(ys < r_ys) ys = r_ys;
|
||||
if(ye > r_ye) ye = r_ye;
|
||||
if(bmatch != ~0U)
|
||||
for(unsigned int y = (unsigned int) ys; y <= (unsigned int)ye; y++)
|
||||
{
|
||||
uint32 tmpcolor = color;
|
||||
if(buf[x + (y + y1) * surface->pitch32] == bmatch) tmpcolor = breplace;
|
||||
if(buf[x + (y + y1) * surface->pitch32] != breplace)
|
||||
buf[x + (y + y1) * surface->pitch32] = tmpcolor;
|
||||
}
|
||||
else
|
||||
for(unsigned int y = (unsigned int)ys; y <= (unsigned int)ye; y++)
|
||||
buf[x + (y + y1) * surface->pitch32] = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
ys = round(ys);
|
||||
ye = round(ye);
|
||||
|
||||
if(ys > r_ys) ys = r_ys;
|
||||
if(ye < r_ye) ye = r_ye;
|
||||
|
||||
if(bmatch != ~0U)
|
||||
for(int y = (int)ys; y >= (int)ye; y--)
|
||||
{
|
||||
uint32 tmpcolor = color;
|
||||
|
||||
if(buf[x + (y + y1) * surface->pitch32] == bmatch)
|
||||
tmpcolor = breplace;
|
||||
|
||||
if(buf[x + (y + y1) * surface->pitch32] != breplace)
|
||||
buf[x + (y + y1) * surface->pitch32] = tmpcolor;
|
||||
}
|
||||
else
|
||||
for(int y = (int)ys; y >= (int)ye; y--)
|
||||
{
|
||||
buf[x + (y + y1) * surface->pitch32] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Player_Init(int tsongs, const std::string &album, const std::string &artist, const std::string ©right, const std::vector<std::string> &snames)
|
||||
{
|
||||
AlbumName = album;
|
||||
Artist = artist;
|
||||
Copyright = copyright;
|
||||
SongNames = snames;
|
||||
|
||||
TotalSongs = tsongs;
|
||||
|
||||
MDFNGameInfo->nominal_width = 384;
|
||||
MDFNGameInfo->nominal_height = 240;
|
||||
|
||||
MDFNGameInfo->fb_width = 384;
|
||||
MDFNGameInfo->fb_height = 240;
|
||||
|
||||
MDFNGameInfo->lcm_width = 384;
|
||||
MDFNGameInfo->lcm_height = 240;
|
||||
|
||||
MDFNGameInfo->GameType = GMT_PLAYER;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
int Player_Init(int tsongs, const std::string &album, const std::string &artist, const std::string ©right, char **snames)
|
||||
{
|
||||
std::vector<std::string> tmpvec;
|
||||
|
||||
if(snames)
|
||||
{
|
||||
for(int i = 0; i < tsongs; i++)
|
||||
tmpvec.push_back(snames[i] ? snames[i] : "");
|
||||
}
|
||||
|
||||
return Player_Init(tsongs, album, artist, copyright, tmpvec);
|
||||
}
|
||||
|
||||
void Player_Draw(MDFN_Surface *surface, MDFN_Rect *dr, int CurrentSong, int16 *samples, int32 sampcount)
|
||||
{
|
||||
uint32 *XBuf = surface->pixels;
|
||||
//MDFN_Rect *dr = &MDFNGameInfo->DisplayRect;
|
||||
int x,y;
|
||||
const uint32 text_color = surface->MakeColor(0xE8, 0xE8, 0xE8);
|
||||
const uint32 text_shadow_color = surface->MakeColor(0x00, 0x18, 0x10);
|
||||
const uint32 bg_color = surface->MakeColor(0x20, 0x00, 0x08);
|
||||
const uint32 left_color = surface->MakeColor(0x80, 0x80, 0xFF);
|
||||
const uint32 right_color = surface->MakeColor(0x80, 0xff, 0x80);
|
||||
const uint32 center_color = surface->MakeColor(0x80, 0xCC, 0xCC);
|
||||
|
||||
dr->x = 0;
|
||||
dr->y = 0;
|
||||
dr->w = 384;
|
||||
dr->h = 240;
|
||||
|
||||
// Draw the background color
|
||||
for(y = 0; y < dr->h; y++)
|
||||
MDFN_FastU32MemsetM8(&XBuf[y * surface->pitch32], bg_color, dr->w);
|
||||
|
||||
// Now we draw the waveform data. It should be centered vertically, and extend the screen horizontally from left to right.
|
||||
int32 x_scale;
|
||||
float y_scale;
|
||||
int lastX, lastY;
|
||||
|
||||
|
||||
x_scale = (sampcount << 8) / dr->w;
|
||||
|
||||
y_scale = (float)dr->h;
|
||||
|
||||
if(sampcount)
|
||||
{
|
||||
for(int wc = 0; wc < MDFNGameInfo->soundchan; wc++)
|
||||
{
|
||||
uint32 color = wc ? right_color : left_color; //MK_COLOR(0x80, 0xff, 0x80) : MK_COLOR(0x80, 0x80, 0xFF);
|
||||
|
||||
if(MDFNGameInfo->soundchan == 1)
|
||||
color = center_color; //MK_COLOR(0x80, 0xc0, 0xc0);
|
||||
|
||||
lastX = -1;
|
||||
lastY = 0;
|
||||
|
||||
for(x = 0; x < dr->w; x++)
|
||||
{
|
||||
float samp = ((float)-samples[wc + (x * x_scale >> 8) * MDFNGameInfo->soundchan]) / 32768;
|
||||
int ypos;
|
||||
|
||||
ypos = (dr->h / 2) + (int)(y_scale * samp);
|
||||
|
||||
if(ypos >= dr->h)
|
||||
ypos = dr->h - 1;
|
||||
|
||||
if(ypos < 0)
|
||||
ypos = 0;
|
||||
|
||||
if(lastX >= 0)
|
||||
DrawLine(surface, color, wc ? left_color : ~0, center_color, lastX, lastY, x, ypos);
|
||||
lastX = x;
|
||||
lastY = ypos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Quick warning: DrawTextTransShadow() has the possibility of drawing past the visible display area by 1 pixel on each axis. This should only be a cosmetic issue
|
||||
// if 1-pixel line overflowing occurs onto the next line. Fixme in the future?
|
||||
|
||||
XBuf += 2 * surface->pitch32;
|
||||
DrawTextTransShadow(XBuf, surface->pitch32 * sizeof(uint32), dr->w, AlbumName, text_color, text_shadow_color, 1);
|
||||
|
||||
XBuf += (13 + 2) * surface->pitch32;
|
||||
DrawTextTransShadow(XBuf, surface->pitch32 * sizeof(uint32), dr->w, Artist, text_color, text_shadow_color, 1);
|
||||
|
||||
XBuf += (13 + 2) * surface->pitch32;
|
||||
DrawTextTransShadow(XBuf, surface->pitch32 * sizeof(uint32), dr->w, Copyright, text_color, text_shadow_color, 1);
|
||||
|
||||
XBuf += (13 * 2) * surface->pitch32;
|
||||
|
||||
// If each song has an individual name, show this song's name.
|
||||
{
|
||||
std::string tmpsong = "";
|
||||
|
||||
if((unsigned int)CurrentSong < SongNames.size())
|
||||
tmpsong = SongNames[CurrentSong];
|
||||
|
||||
if(tmpsong == "" && TotalSongs > 1)
|
||||
tmpsong = std::string(_("Song:"));
|
||||
|
||||
DrawTextTransShadow(XBuf, surface->pitch32 * sizeof(uint32), dr->w, tmpsong, text_color, text_shadow_color, 1);
|
||||
}
|
||||
|
||||
XBuf += (13 + 2) * surface->pitch32;
|
||||
if(TotalSongs > 1)
|
||||
{
|
||||
char snbuf[32];
|
||||
snprintf(snbuf, 32, "<%d/%d>", CurrentSong + 1, TotalSongs);
|
||||
DrawTextTransShadow(XBuf, surface->pitch32 * sizeof(uint32), dr->w, (uint8*)snbuf, text_color, text_shadow_color, 1);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#ifndef __MDFN_PLAYER_H
|
||||
#define __MDFN_PLAYER_H
|
||||
|
||||
int Player_Init(int tsongs, const std::string &album, const std::string &artist, const std::string ©right,const std::vector<std::string> &snames = std::vector<std::string>());
|
||||
int Player_Init(int tsongs, const std::string &album, const std::string &artist, const std::string ©right, char **snames);
|
||||
|
||||
void Player_Draw(MDFN_Surface *surface, MDFN_Rect *dr, int CurrentSong, int16 *samples, int32 sampcount);
|
||||
|
||||
#endif
|
@ -15,6 +15,10 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __LIBRETRO__
|
||||
#define HAVE_PSF 1
|
||||
#endif
|
||||
|
||||
#include "psx.h"
|
||||
#include "mdec.h"
|
||||
#include "frontio.h"
|
||||
@ -24,8 +28,11 @@
|
||||
#include "spu.h"
|
||||
#include "../mednafen-endian.h"
|
||||
#include "../mempatcher.h"
|
||||
#ifdef HAVE_PSF
|
||||
#include "../PSFLoader.h"
|
||||
#include "../player.h"
|
||||
#endif
|
||||
|
||||
#include "../cputest/cputest.h"
|
||||
|
||||
extern MDFNGI EmulatedPSX;
|
||||
@ -33,6 +40,7 @@ extern MDFNGI EmulatedPSX;
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
#ifdef HAVE_PSF
|
||||
class PSF1Loader : public PSFLoader
|
||||
{
|
||||
public:
|
||||
@ -44,6 +52,7 @@ class PSF1Loader : public PSFLoader
|
||||
|
||||
PSFTags tags;
|
||||
};
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
@ -58,7 +67,10 @@ static uint32 ReadCounter = 0;
|
||||
static uint32 WriteCounter = 0;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PSF
|
||||
static PSF1Loader *psf_loader = NULL;
|
||||
#endif
|
||||
|
||||
static std::vector<CDIF*> *cdifs = NULL;
|
||||
static std::vector<const char *> cdifs_scex_ids;
|
||||
static bool CD_TrayOpen;
|
||||
@ -780,11 +792,19 @@ static void Emulate(EmulateSpecStruct *espec)
|
||||
espec->SoundBufSize = 0;
|
||||
|
||||
FIO->UpdateInput();
|
||||
#ifdef HAVE_PSF
|
||||
GPU->StartFrame(psf_loader ? NULL : espec);
|
||||
#else
|
||||
GPU->StartFrame(espec);
|
||||
#endif
|
||||
SPU->StartFrame(espec->SoundRate, MDFN_GetSettingUI("psx.spu.resamp_quality"));
|
||||
|
||||
Running = -1;
|
||||
#ifdef HAVE_PSF
|
||||
timestamp = CPU->Run(timestamp, psf_loader != NULL);
|
||||
#else
|
||||
timestamp = CPU->Run(timestamp, false);
|
||||
#endif
|
||||
|
||||
assert(timestamp);
|
||||
|
||||
@ -806,6 +826,7 @@ static void Emulate(EmulateSpecStruct *espec)
|
||||
|
||||
espec->MasterCycles = timestamp;
|
||||
|
||||
#ifdef HAVE_PSF
|
||||
if(psf_loader)
|
||||
{
|
||||
if(!espec->skip)
|
||||
@ -814,6 +835,7 @@ static void Emulate(EmulateSpecStruct *espec)
|
||||
Player_Draw(espec->surface, &espec->DisplayRect, 0, espec->SoundBuf, espec->SoundBufSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Save memcards if dirty.
|
||||
for(int i = 0; i < 8; i++)
|
||||
@ -867,8 +889,10 @@ static void Emulate(EmulateSpecStruct *espec)
|
||||
|
||||
static bool TestMagic(const char *name, MDFNFILE *fp)
|
||||
{
|
||||
#ifdef HAVE_PSF
|
||||
if(PSFLoader::TestMagic(0x01, fp))
|
||||
return(true);
|
||||
#endif
|
||||
|
||||
if(fp->size < 0x800)
|
||||
return(false);
|
||||
@ -1433,6 +1457,7 @@ static void LoadEXE(const uint8 *data, const uint32 size, bool ignore_pcsp = fal
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_PSF
|
||||
PSF1Loader::PSF1Loader(MDFNFILE *fp)
|
||||
{
|
||||
tags = Load(0x01, 2033664, fp);
|
||||
@ -1447,11 +1472,16 @@ void PSF1Loader::HandleEXE(const uint8 *data, uint32 size, bool ignore_pcsp)
|
||||
{
|
||||
LoadEXE(data, size, ignore_pcsp);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Cleanup(void);
|
||||
static int Load(const char *name, MDFNFILE *fp)
|
||||
{
|
||||
#ifdef HAVE_PSF
|
||||
const bool IsPSF = PSFLoader::TestMagic(0x01, fp);
|
||||
#else
|
||||
const bool IsPSF = false;
|
||||
#endif
|
||||
|
||||
if(!TestMagic(name, fp))
|
||||
{
|
||||
@ -1480,6 +1510,7 @@ static int Load(const char *name, MDFNFILE *fp)
|
||||
|
||||
try
|
||||
{
|
||||
#ifdef HAVE_PSF
|
||||
if(IsPSF)
|
||||
{
|
||||
psf_loader = new PSF1Loader(fp);
|
||||
@ -1491,6 +1522,7 @@ static int Load(const char *name, MDFNFILE *fp)
|
||||
Player_Init(1, psf_loader->tags.GetTag("game"), psf_loader->tags.GetTag("artist"), psf_loader->tags.GetTag("copyright"), SongNames);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
LoadEXE(fp->data, fp->size);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
@ -1520,11 +1552,13 @@ static void Cleanup(void)
|
||||
{
|
||||
TextMem.resize(0);
|
||||
|
||||
#ifdef HAVE_PSF
|
||||
if(psf_loader)
|
||||
{
|
||||
delete psf_loader;
|
||||
psf_loader = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(CDC)
|
||||
{
|
||||
@ -1575,7 +1609,9 @@ static void Cleanup(void)
|
||||
|
||||
static void CloseGame(void)
|
||||
{
|
||||
#ifdef HAVE_PSF
|
||||
if(!psf_loader)
|
||||
#endif
|
||||
{
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
@ -1601,9 +1637,11 @@ static void CloseGame(void)
|
||||
|
||||
static void SetInput(int port, const char *type, void *ptr)
|
||||
{
|
||||
#ifdef HAVE_PSF
|
||||
if(psf_loader)
|
||||
FIO->SetInput(port, "none", NULL);
|
||||
else
|
||||
#endif
|
||||
FIO->SetInput(port, type, ptr);
|
||||
}
|
||||
|
||||
@ -1719,8 +1757,10 @@ static void DoSimpleCommand(int cmd)
|
||||
|
||||
static const FileExtensionSpecStruct KnownExtensions[] =
|
||||
{
|
||||
#ifdef HAVE_PSF
|
||||
{ ".psf", gettext_noop("PSF1 Rip") },
|
||||
{ ".minipsf", gettext_noop("MiniPSF1 Rip") },
|
||||
#endif
|
||||
{ ".psx", gettext_noop("PS-X Executable") },
|
||||
{ ".exe", gettext_noop("PS-X Executable") },
|
||||
{ NULL, NULL }
|
||||
|
@ -1,990 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "mednafen.h"
|
||||
#include "mednafen-endian.h"
|
||||
#include "qtrecord.h"
|
||||
#include "compress/minilzo.h"
|
||||
#include "video/png.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <zlib.h>
|
||||
|
||||
void QTRecord::w8(uint8 val)
|
||||
{
|
||||
qtfile.put_char(val);
|
||||
}
|
||||
|
||||
void QTRecord::w16(uint16 val)
|
||||
{
|
||||
uint8 buf[2];
|
||||
|
||||
MDFN_en16msb(buf, val);
|
||||
|
||||
qtfile.write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void QTRecord::w32(uint32 val)
|
||||
{
|
||||
uint8 buf[4];
|
||||
|
||||
MDFN_en32msb(buf, val);
|
||||
|
||||
qtfile.write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void QTRecord::w32s(const char *str)
|
||||
{
|
||||
uint8 buf[4];
|
||||
|
||||
memset(buf, 0x20, sizeof(buf));
|
||||
|
||||
for(unsigned int i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
if(!str[i])
|
||||
break;
|
||||
|
||||
buf[i] = str[i];
|
||||
}
|
||||
|
||||
qtfile.write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void QTRecord::w64s(const char *str)
|
||||
{
|
||||
uint8 buf[8];
|
||||
|
||||
memset(buf, 0x20, sizeof(buf));
|
||||
|
||||
for(unsigned int i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
if(!str[i])
|
||||
break;
|
||||
|
||||
buf[i] = str[i];
|
||||
}
|
||||
|
||||
qtfile.write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
|
||||
void QTRecord::w64(uint64 val)
|
||||
{
|
||||
uint8 buf[8];
|
||||
|
||||
MDFN_en64msb(buf, val);
|
||||
|
||||
qtfile.write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
// max_len doesn't include the leading 1-byte count
|
||||
// returns number of bytes written.
|
||||
uint32 QTRecord::wps(const char *str, uint32 max_len)
|
||||
{
|
||||
if(!max_len) // Variable-size pascal string
|
||||
{
|
||||
max_len = strlen(str);
|
||||
if(max_len > 255)
|
||||
max_len = 255;
|
||||
}
|
||||
|
||||
uint32 count = strlen(str);
|
||||
char buf[1 + max_len];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if(count > max_len)
|
||||
count = max_len;
|
||||
|
||||
strncpy(buf + 1, str, count);
|
||||
|
||||
buf[0] = count;
|
||||
|
||||
qtfile.write(buf, sizeof(buf));
|
||||
|
||||
return(sizeof(buf));
|
||||
}
|
||||
|
||||
void QTRecord::vardata_begin(void)
|
||||
{
|
||||
vardata_foffsets.push_back(qtfile.tell());
|
||||
|
||||
w32(0); // Overwritten in vardata_end()
|
||||
}
|
||||
|
||||
void QTRecord::vardata_end(void)
|
||||
{
|
||||
int64 cur_offset = qtfile.tell();
|
||||
int64 start_offset = vardata_foffsets.back();
|
||||
|
||||
vardata_foffsets.pop_back();
|
||||
|
||||
qtfile.seek(start_offset, SEEK_SET);
|
||||
w32(cur_offset - start_offset);
|
||||
|
||||
//printf("%d\n", cur_offset - start_offset);
|
||||
|
||||
qtfile.seek(cur_offset, SEEK_SET);
|
||||
}
|
||||
|
||||
void QTRecord::atom_begin(uint32 type, bool small_atom)
|
||||
{
|
||||
//small_atom = true; // DEBUG, REMOVE ME
|
||||
|
||||
atom_foffsets.push_back(qtfile.tell());
|
||||
atom_smalls.push_back(small_atom);
|
||||
|
||||
if(small_atom)
|
||||
{
|
||||
w32(0);
|
||||
w32(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
w32(0x00000001);
|
||||
w32(type);
|
||||
w64(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QTRecord::atom_begin(const char *type, bool small_atom)
|
||||
{
|
||||
uint32 type_num = 0;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(!type[i])
|
||||
break;
|
||||
|
||||
type_num |= (uint32)type[i] << ((3 - i) * 8);
|
||||
}
|
||||
atom_begin(type_num, small_atom);
|
||||
}
|
||||
|
||||
void QTRecord::atom_end(void)
|
||||
{
|
||||
int64 cur_offset = qtfile.tell();
|
||||
int64 start_offset = atom_foffsets.back();
|
||||
bool small_atom = atom_smalls.back();
|
||||
|
||||
atom_foffsets.pop_back();
|
||||
atom_smalls.pop_back();
|
||||
|
||||
if(small_atom)
|
||||
{
|
||||
qtfile.seek(start_offset, SEEK_SET);
|
||||
w32(cur_offset - start_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
qtfile.seek(start_offset + 8, SEEK_SET);
|
||||
|
||||
w64(cur_offset - start_offset);
|
||||
}
|
||||
|
||||
qtfile.seek(cur_offset, SEEK_SET);
|
||||
}
|
||||
|
||||
QTRecord::QTRecord(const char *path, const VideoSpec &spec) : qtfile(path, FileWrapper::MODE_WRITE_SAFE)
|
||||
{
|
||||
Finished = false;
|
||||
|
||||
SoundFramesWritten = 0;
|
||||
|
||||
SoundRate = spec.SoundRate;
|
||||
SoundChan = spec.SoundChan;
|
||||
|
||||
QTVideoWidth = spec.VideoWidth;
|
||||
QTVideoHeight = spec.VideoHeight;
|
||||
|
||||
A = 65536 * spec.AspectXAdjust;
|
||||
D = 65536 * spec.AspectYAdjust;
|
||||
|
||||
VideoCodec = spec.VideoCodec;
|
||||
|
||||
if(VideoCodec == VCODEC_PNG)
|
||||
RawVideoBuffer.resize((1 + QTVideoWidth * 3) * QTVideoHeight);
|
||||
else
|
||||
RawVideoBuffer.resize(QTVideoWidth * QTVideoHeight * 3);
|
||||
|
||||
if(VideoCodec == VCODEC_CSCD)
|
||||
CompressedVideoBuffer.resize((RawVideoBuffer.size() * 110 + 99 ) / 100); // 1.10
|
||||
else if(VideoCodec == VCODEC_PNG)
|
||||
CompressedVideoBuffer.resize(compressBound(RawVideoBuffer.size()));
|
||||
|
||||
{
|
||||
int64 unixy_time = time(NULL);
|
||||
uint32 appley_time;
|
||||
|
||||
//if(unixy_time == (time_t)-1) // TODO: handle error
|
||||
|
||||
//printf("%d\n", unixy_time);
|
||||
|
||||
appley_time = unixy_time + 2082844800;
|
||||
|
||||
CreationTS = appley_time;
|
||||
ModificationTS = appley_time;
|
||||
}
|
||||
|
||||
Write_ftyp();
|
||||
|
||||
atom_begin("mdat", false);
|
||||
}
|
||||
|
||||
|
||||
void QTRecord::WriteFrame(const MDFN_Surface *surface, const MDFN_Rect &DisplayRect, const MDFN_Rect *LineWidths,
|
||||
const int16 *SoundBuf, const int32 SoundBufSize)
|
||||
{
|
||||
QTChunk qts;
|
||||
|
||||
memset(&qts, 0, sizeof(qts));
|
||||
|
||||
if(DisplayRect.h <= 0)
|
||||
{
|
||||
fprintf(stderr, "[BUG] qtrecord.cpp: DisplayRect.h <= 0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
qts.video_foffset = qtfile.tell();
|
||||
|
||||
// Write video here
|
||||
{
|
||||
uint32 dest_y = 0;
|
||||
int yscale_factor = QTVideoHeight / DisplayRect.h;
|
||||
|
||||
for(int y = DisplayRect.y; y < DisplayRect.y + DisplayRect.h; y++)
|
||||
{
|
||||
int x_start;
|
||||
int width;
|
||||
int xscale_factor;
|
||||
int32 dest_x;
|
||||
uint32 *src_ptr;
|
||||
uint8 *dest_line;
|
||||
|
||||
if(dest_y >= QTVideoHeight)
|
||||
break;
|
||||
|
||||
if(VideoCodec == VCODEC_CSCD)
|
||||
dest_line = &RawVideoBuffer[(QTVideoHeight - 1 - dest_y) * QTVideoWidth * 3];
|
||||
else if(VideoCodec == VCODEC_PNG)
|
||||
dest_line = &RawVideoBuffer[dest_y * (QTVideoWidth * 3 + 1)];
|
||||
else
|
||||
dest_line = &RawVideoBuffer[dest_y * QTVideoWidth * 3];
|
||||
|
||||
if(LineWidths[0].w == ~0)
|
||||
{
|
||||
x_start = DisplayRect.x;
|
||||
width = DisplayRect.w;
|
||||
}
|
||||
else
|
||||
{
|
||||
x_start = LineWidths[y].x;
|
||||
width = LineWidths[y].w;
|
||||
}
|
||||
|
||||
xscale_factor = QTVideoWidth / width;
|
||||
|
||||
dest_x = 0;
|
||||
|
||||
src_ptr = surface->pixels + y * surface->pitchinpix + x_start;
|
||||
|
||||
if(VideoCodec == VCODEC_PNG)
|
||||
{
|
||||
*dest_line = 0;
|
||||
dest_line++;
|
||||
}
|
||||
|
||||
#if 0
|
||||
while(dest_x < ((QTVideoWidth - (xscale_factor * width)) / 2))
|
||||
{
|
||||
dest_line[dest_x * 3 + 0] = 0;
|
||||
dest_line[dest_x * 3 + 1] = 0;
|
||||
dest_line[dest_x * 3 + 2] = 0;
|
||||
|
||||
dest_x++;
|
||||
}
|
||||
#endif
|
||||
|
||||
for(int x = x_start; x < x_start + width; x++)
|
||||
{
|
||||
for(int sub_x = 0; sub_x < xscale_factor; sub_x++)
|
||||
{
|
||||
if(dest_x < QTVideoWidth)
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
surface->DecodeColor(*src_ptr, r, g, b, a);
|
||||
|
||||
if(VideoCodec == VCODEC_CSCD)
|
||||
{
|
||||
dest_line[dest_x * 3 + 0] = b;
|
||||
dest_line[dest_x * 3 + 1] = g;
|
||||
dest_line[dest_x * 3 + 2] = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest_line[dest_x * 3 + 0] = r;
|
||||
dest_line[dest_x * 3 + 1] = g;
|
||||
dest_line[dest_x * 3 + 2] = b;
|
||||
}
|
||||
|
||||
dest_x++;
|
||||
}
|
||||
}
|
||||
src_ptr++;
|
||||
}
|
||||
|
||||
while(dest_x < QTVideoWidth)
|
||||
{
|
||||
dest_line[dest_x * 3 + 0] = 0;
|
||||
dest_line[dest_x * 3 + 1] = 0;
|
||||
dest_line[dest_x * 3 + 2] = 0;
|
||||
|
||||
dest_x++;
|
||||
}
|
||||
|
||||
for(int sub_y = 1; sub_y < yscale_factor; sub_y++)
|
||||
{
|
||||
if((dest_y + sub_y) >= QTVideoHeight)
|
||||
break;
|
||||
|
||||
if(VideoCodec == VCODEC_CSCD)
|
||||
memcpy(&RawVideoBuffer[(QTVideoHeight - 1 - (dest_y + sub_y)) * QTVideoWidth * 3], dest_line, QTVideoWidth * 3);
|
||||
else if(VideoCodec == VCODEC_PNG)
|
||||
memcpy(&RawVideoBuffer[(dest_y + sub_y) * (QTVideoWidth * 3 + 1)], dest_line - 1, QTVideoWidth * 3 + 1);
|
||||
else
|
||||
memcpy(&RawVideoBuffer[(dest_y + sub_y) * QTVideoWidth * 3], dest_line, QTVideoWidth * 3);
|
||||
}
|
||||
|
||||
dest_y += yscale_factor;
|
||||
} // end for(int y = DisplayRect.y; y < DisplayRect.y + DisplayRect.h; y++)
|
||||
}
|
||||
|
||||
if(VideoCodec == VCODEC_CSCD)
|
||||
{
|
||||
static uint8 workmem[LZO1X_1_MEM_COMPRESS];
|
||||
lzo_uint dst_len = CompressedVideoBuffer.size();
|
||||
|
||||
qtfile.put_char((0 << 1) | 0x1);
|
||||
qtfile.put_char(0);
|
||||
|
||||
lzo1x_1_compress(&RawVideoBuffer[0], RawVideoBuffer.size(), &CompressedVideoBuffer[0], &dst_len, workmem);
|
||||
|
||||
qtfile.write(&CompressedVideoBuffer[0], dst_len);
|
||||
}
|
||||
else if(VideoCodec == VCODEC_RAW)
|
||||
qtfile.write(&RawVideoBuffer[0], RawVideoBuffer.size());
|
||||
else if(VideoCodec == VCODEC_PNG)
|
||||
{
|
||||
//PNGWrite(qtfile, surface, DisplayRect, LineWidths);
|
||||
static const uint8 png_sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
|
||||
uint8 IHDR[13];
|
||||
uLongf compress_buffer_size;
|
||||
|
||||
qtfile.write(png_sig, sizeof(png_sig));
|
||||
|
||||
MDFN_en32msb(&IHDR[0], QTVideoWidth);
|
||||
MDFN_en32msb(&IHDR[4], QTVideoHeight);
|
||||
|
||||
IHDR[8] = 8; // 8 bits per color component
|
||||
IHDR[9] = 2; // Color type: RGB triplet(no alpha)
|
||||
IHDR[10] = 0; // Compression: deflate
|
||||
IHDR[11] = 0; // Basic adaptive filter set
|
||||
IHDR[12] = 0; // No interlace
|
||||
|
||||
|
||||
PNGWrite::WriteChunk(qtfile, 13, "IHDR", IHDR);
|
||||
|
||||
compress_buffer_size = CompressedVideoBuffer.size();
|
||||
|
||||
compress(&CompressedVideoBuffer[0], &compress_buffer_size, &RawVideoBuffer[0], RawVideoBuffer.size());
|
||||
|
||||
PNGWrite::WriteChunk(qtfile, compress_buffer_size, "IDAT", &CompressedVideoBuffer[0]);
|
||||
|
||||
PNGWrite::WriteChunk(qtfile, 0, "IEND", 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
qts.video_byte_size = qtfile.tell() - qts.video_foffset;
|
||||
|
||||
|
||||
|
||||
qts.audio_foffset = qtfile.tell();
|
||||
|
||||
// Write audio here
|
||||
{
|
||||
int16 abuf[SoundBufSize * SoundChan];
|
||||
|
||||
for(int i = 0; i < SoundBufSize * SoundChan; i++)
|
||||
MDFN_en16msb((uint8 *)&abuf[i], SoundBuf[i]);
|
||||
|
||||
qtfile.write(abuf, sizeof(abuf));
|
||||
}
|
||||
|
||||
qts.audio_byte_size = qtfile.tell() - qts.audio_foffset;
|
||||
|
||||
|
||||
QTChunks.push_back(qts);
|
||||
|
||||
SoundFramesWritten += SoundBufSize;
|
||||
}
|
||||
|
||||
void QTRecord::Write_ftyp(void) // Leaf
|
||||
{
|
||||
atom_begin("ftyp");
|
||||
|
||||
w32s("qt "); // Major brand
|
||||
|
||||
w32(0x20070900); // Minor_Version
|
||||
|
||||
w32s("qt "); // Compatible brand
|
||||
|
||||
// Placeholders to get the mdat start at 0x20 for prettiness(and libquicktime does it, I don't know if it has another reason).
|
||||
w32(0);
|
||||
w32(0);
|
||||
w32(0);
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_mvhd(void) // Leaf
|
||||
{
|
||||
atom_begin("mvhd");
|
||||
|
||||
w32(0); // Version/flags
|
||||
w32(CreationTS); // Created Mac date
|
||||
w32(ModificationTS); // Modified Mac date
|
||||
w32(SoundRate); // Time scale
|
||||
|
||||
w32(SoundFramesWritten); // Duration
|
||||
w32(65536 * 1); // Preferred rate
|
||||
w16(256 * 1); // Preferred volume
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
w16(0); // Reserved(5 * 2 = 10)
|
||||
|
||||
w32(65536 * 1); // A
|
||||
w32(0); // B
|
||||
w32(0); // U
|
||||
w32(0); // C
|
||||
w32(65536 * 1); // D
|
||||
w32(0); // V
|
||||
w32(0); // X
|
||||
w32(0); // Y
|
||||
w32(1 << 30); // W
|
||||
|
||||
w32(0); // Preview time
|
||||
w32(0); // Preview duration
|
||||
|
||||
w32(0); // Poster time
|
||||
|
||||
w32(0); // Selection time.
|
||||
w32(SoundFramesWritten); // Selection duration.
|
||||
|
||||
w32(0); // Current time
|
||||
|
||||
w32(3); // Next track id
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_tkhd(void) // Leaf
|
||||
{
|
||||
atom_begin("tkhd");
|
||||
|
||||
w32(0xF); // Version and flags
|
||||
|
||||
w32(CreationTS); // Created mac date
|
||||
w32(ModificationTS); // Modified mac date
|
||||
|
||||
if(OnAudioTrack)
|
||||
w32(2);
|
||||
else
|
||||
w32(1); // Track id
|
||||
|
||||
w32(0); // Reserved
|
||||
|
||||
w32(SoundFramesWritten); // Duration
|
||||
|
||||
w64(0); // Reserved
|
||||
|
||||
w16(0); // Video layer.
|
||||
w16(0); // Alternate/other
|
||||
w16(256); // Track audio volume
|
||||
w16(0); // Reserved
|
||||
|
||||
w32(A); // A
|
||||
w32(0); // B
|
||||
w32(0); // U
|
||||
w32(0); // C
|
||||
w32(D); // D
|
||||
w32(0); // V
|
||||
w32(0); // X
|
||||
w32(0); // Y
|
||||
w32(1 << 30); // W
|
||||
|
||||
w32(65536 * QTVideoWidth);
|
||||
w32(65536 * QTVideoHeight);
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Sample description
|
||||
void QTRecord::Write_stsd(void) // Leaf
|
||||
{
|
||||
atom_begin("stsd");
|
||||
|
||||
w32(0); // Version and flags
|
||||
|
||||
w32(1); // Number of sample descriptions
|
||||
|
||||
if(OnAudioTrack) // Audio track
|
||||
{
|
||||
vardata_begin(); // w32(36)
|
||||
|
||||
w32s("twos"); // Data format
|
||||
|
||||
w32(0); // Reserved
|
||||
w16(0); // Reserved
|
||||
|
||||
w16(1); // dref index?
|
||||
|
||||
w16(0); // Version
|
||||
w16(0); // Revision level
|
||||
|
||||
w32s("MDFN"); // Vendor
|
||||
|
||||
w16(SoundChan); // Number of sound channels
|
||||
w16(16); // Sample size
|
||||
w16(0); // Audio compression ID
|
||||
w16(0); // Audio packet rate
|
||||
w32(SoundRate * 65536); // Audio sample rate
|
||||
|
||||
vardata_end();
|
||||
}
|
||||
else // Video track
|
||||
{
|
||||
vardata_begin(); // w32(86 + 12); // Description length (+12 for gama)
|
||||
|
||||
if(VideoCodec == VCODEC_CSCD)
|
||||
w32s("CSCD"); // Data format
|
||||
else if(VideoCodec == VCODEC_PNG)
|
||||
w32s("png ");
|
||||
else
|
||||
w32s("raw "); // Data format
|
||||
|
||||
w32(0); // Reserved
|
||||
w16(0); // Reserved
|
||||
|
||||
w16(1); // dref index?
|
||||
|
||||
w16(0); // Version
|
||||
w16(0); // Revision level
|
||||
|
||||
w32s("MDFN"); // Vendor
|
||||
|
||||
w32(1024); // Video temporal quality
|
||||
w32(1024); // Video spatial quality
|
||||
|
||||
w16(QTVideoWidth); // Width of source image
|
||||
w16(QTVideoHeight); // Height of source image
|
||||
|
||||
w32(48 * 65536); // Horizontal PPI(FIXME)
|
||||
|
||||
w32(48 * 65536); // Vertical PPI(FIXME)
|
||||
|
||||
w32(0); // Data size must be set to 0
|
||||
|
||||
w16(1); // Frame count(per sample)
|
||||
|
||||
wps("Mednafen " MEDNAFEN_VERSION, 31); // Video encoder
|
||||
|
||||
w16(24); // Depth
|
||||
|
||||
w16(0xFFFF); // Color table ID
|
||||
|
||||
atom_begin("gama");
|
||||
w32(65536 * 2.2);
|
||||
atom_end();
|
||||
|
||||
vardata_end();
|
||||
}
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Time-to-sample
|
||||
void QTRecord::Write_stts(void) // Leaf
|
||||
{
|
||||
atom_begin("stts");
|
||||
|
||||
w32(0); // Version and flags
|
||||
|
||||
if(OnAudioTrack)
|
||||
{
|
||||
w32(1); // Number of entries
|
||||
|
||||
// Entry
|
||||
w32(SoundFramesWritten);
|
||||
w32(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
w32(QTChunks.size()); // number of entries
|
||||
|
||||
for(uint32 i = 0; i < QTChunks.size(); i++)
|
||||
{
|
||||
w32(1);
|
||||
w32(QTChunks[i].audio_byte_size / SoundChan / sizeof(int16));
|
||||
}
|
||||
}
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Sample-to-chunk
|
||||
void QTRecord::Write_stsc(void) // Leaf
|
||||
{
|
||||
atom_begin("stsc");
|
||||
|
||||
w32(0); // Version and flags
|
||||
|
||||
if(OnAudioTrack)
|
||||
{
|
||||
w32(QTChunks.size()); // Number of entries
|
||||
for(uint32 i = 0; i < QTChunks.size(); i++)
|
||||
{
|
||||
w32(1 + i); // First chunk number using this entry
|
||||
w32(QTChunks[i].audio_byte_size / SoundChan / sizeof(int16)); // Samples per chunk
|
||||
w32(1); // Sample description ID(references stsd)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
w32(1); // Number of entries
|
||||
w32(1); // First chunk
|
||||
w32(1); // Samples per chunk
|
||||
w32(1); // Sample description ID(references data in stsd)
|
||||
}
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_stsz(void) // Leaf
|
||||
{
|
||||
atom_begin("stsz");
|
||||
|
||||
w32(0); // Version and flags
|
||||
|
||||
if(OnAudioTrack)
|
||||
{
|
||||
w32(1);
|
||||
w32(SoundFramesWritten);
|
||||
}
|
||||
else
|
||||
{
|
||||
w32(0); // Sample size
|
||||
|
||||
w32(QTChunks.size()); // Number of entries
|
||||
|
||||
for(uint32 i = 0; i < QTChunks.size(); i++)
|
||||
{
|
||||
if(OnAudioTrack)
|
||||
w32(QTChunks[i].audio_byte_size);
|
||||
else
|
||||
w32(QTChunks[i].video_byte_size);
|
||||
}
|
||||
}
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Chunk offset atom(64-bit style)
|
||||
void QTRecord::Write_co64(void) // Leaf
|
||||
{
|
||||
atom_begin("co64");
|
||||
|
||||
w32(0); // Version and flags
|
||||
|
||||
w32(QTChunks.size()); // Number of entries
|
||||
|
||||
for(uint32 i = 0; i < QTChunks.size(); i++)
|
||||
{
|
||||
if(OnAudioTrack)
|
||||
w64(QTChunks[i].audio_foffset);
|
||||
else
|
||||
w64(QTChunks[i].video_foffset);
|
||||
}
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_stco(void) // Leaf
|
||||
{
|
||||
atom_begin("stco");
|
||||
|
||||
w32(0); // Version and flags
|
||||
|
||||
w32(QTChunks.size()); // Number of entries
|
||||
|
||||
for(uint32 i = 0; i < QTChunks.size(); i++)
|
||||
{
|
||||
if(OnAudioTrack)
|
||||
w32(QTChunks[i].audio_foffset);
|
||||
else
|
||||
w32(QTChunks[i].video_foffset);
|
||||
}
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_stbl(void)
|
||||
{
|
||||
atom_begin("stbl");
|
||||
|
||||
Write_stsd();
|
||||
|
||||
Write_stts();
|
||||
|
||||
Write_stsc();
|
||||
|
||||
Write_stsz();
|
||||
|
||||
bool need64bit_offset = false;
|
||||
|
||||
if(OnAudioTrack && QTChunks.back().audio_foffset >= ((int64)1 << 32))
|
||||
need64bit_offset = true;
|
||||
|
||||
if(!OnAudioTrack && QTChunks.back().video_foffset >= ((int64)1 << 32))
|
||||
need64bit_offset = true;
|
||||
|
||||
if(need64bit_offset)
|
||||
Write_co64();
|
||||
else
|
||||
Write_stco();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Media header atom
|
||||
void QTRecord::Write_mdhd(void) // Leaf
|
||||
{
|
||||
atom_begin("mdhd");
|
||||
|
||||
w32(0); // Version/flags
|
||||
w32(CreationTS); // Creation date
|
||||
w32(ModificationTS); // Modification date
|
||||
w32(SoundRate); // Time scale
|
||||
w32(SoundFramesWritten); // Duration
|
||||
|
||||
w16(0); // Language
|
||||
w16(0); // Quality
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Sound media information header
|
||||
void QTRecord::Write_smhd(void) // Leaf
|
||||
{
|
||||
atom_begin("smhd");
|
||||
w32(0x1); // Version/flags
|
||||
w16(0); // Balance
|
||||
w16(0); // Reserved
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
// Video media information header
|
||||
void QTRecord::Write_vmhd(void) // Leaf
|
||||
{
|
||||
atom_begin("vmhd");
|
||||
|
||||
w32(0x1); // Version/flags
|
||||
|
||||
w16(0); // Quickdraw graphics mode (Simple Copy, no dither)
|
||||
|
||||
// RGB values(unused I guess in simple copy?)
|
||||
w16(0x8000);
|
||||
w16(0x8000);
|
||||
w16(0x8000);
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_hdlr(const char *str, const char *comp_name) // Leaf
|
||||
{
|
||||
atom_begin("hdlr");
|
||||
|
||||
w32(0); // Version/flags
|
||||
|
||||
w64s(str);
|
||||
|
||||
w32(0); // Reserved
|
||||
|
||||
w32(0); // reserved
|
||||
|
||||
w32(0); // Reserved
|
||||
|
||||
wps(comp_name, 0);
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_dinf(void)
|
||||
{
|
||||
atom_begin("dinf");
|
||||
|
||||
atom_begin("dref");
|
||||
|
||||
w32(0); // Version/flags
|
||||
w32(1); // Number of references
|
||||
|
||||
atom_begin("alis");
|
||||
w32(0x00000001); // Version/flags
|
||||
atom_end();
|
||||
|
||||
atom_end();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_minf(void)
|
||||
{
|
||||
atom_begin("minf");
|
||||
|
||||
if(OnAudioTrack)
|
||||
Write_smhd();
|
||||
else
|
||||
Write_vmhd();
|
||||
|
||||
Write_hdlr("dhlralis", "Mednafen Alias Data Handler");
|
||||
|
||||
Write_dinf();
|
||||
|
||||
Write_stbl();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_mdia(void)
|
||||
{
|
||||
atom_begin("mdia");
|
||||
|
||||
Write_mdhd();
|
||||
|
||||
if(OnAudioTrack)
|
||||
Write_hdlr("mhlrsoun", "Mednafen Sound Media Handler");
|
||||
else
|
||||
Write_hdlr("mhlrvide", "Mednafen Video Media Handler");
|
||||
|
||||
Write_minf();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_edts(void)
|
||||
{
|
||||
atom_begin("edts");
|
||||
|
||||
atom_begin("elst");
|
||||
w32(0); // version/flags
|
||||
w32(1); // Number of edits
|
||||
w32(SoundFramesWritten); // Duration
|
||||
w32(0); // start time
|
||||
w32(65536 * 1); // Rate
|
||||
atom_end();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_trak(void)
|
||||
{
|
||||
atom_begin("trak");
|
||||
|
||||
Write_tkhd();
|
||||
|
||||
Write_edts();
|
||||
|
||||
Write_mdia();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_udta(void)
|
||||
{
|
||||
atom_begin("udta");
|
||||
|
||||
atom_begin("@fmt");
|
||||
|
||||
qtfile.put_string("Computer-generated via an emulator.");
|
||||
|
||||
atom_end();
|
||||
|
||||
atom_begin("@swr");
|
||||
|
||||
qtfile.put_string("Mednafen " MEDNAFEN_VERSION " -- qtrecord.cpp compiled " __DATE__ " " __TIME__);
|
||||
|
||||
atom_end();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Write_moov(void)
|
||||
{
|
||||
atom_begin("moov");
|
||||
|
||||
Write_mvhd();
|
||||
|
||||
OnAudioTrack = false;
|
||||
Write_trak();
|
||||
|
||||
OnAudioTrack = true;
|
||||
Write_trak();
|
||||
|
||||
Write_udta();
|
||||
|
||||
atom_end();
|
||||
}
|
||||
|
||||
void QTRecord::Finish(void)
|
||||
{
|
||||
if(Finished)
|
||||
return;
|
||||
|
||||
Finished = true;
|
||||
|
||||
atom_end();
|
||||
|
||||
Write_moov();
|
||||
|
||||
qtfile.close();
|
||||
}
|
||||
|
||||
QTRecord::~QTRecord(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
Finish();
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFND_PrintError(e.what());
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
#ifndef __MDFN_QTRECORD_H
|
||||
#define __MDFN_QTRECORD_H
|
||||
|
||||
#include "mednafen.h"
|
||||
#include "FileWrapper.h"
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
class QTRecord
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
VCODEC_RAW = 0,
|
||||
VCODEC_CSCD,
|
||||
VCODEC_PNG
|
||||
};
|
||||
|
||||
struct VideoSpec
|
||||
{
|
||||
uint32 SoundRate;
|
||||
uint32 SoundChan; // Number of sound channels
|
||||
|
||||
uint32 VideoWidth;
|
||||
uint32 VideoHeight;
|
||||
|
||||
double AspectXAdjust;
|
||||
double AspectYAdjust;
|
||||
|
||||
int VideoCodec;
|
||||
};
|
||||
|
||||
QTRecord(const char *path, const VideoSpec &spec_arg);
|
||||
void Finish();
|
||||
~QTRecord();
|
||||
|
||||
void WriteFrame(const MDFN_Surface *surface, const MDFN_Rect &DisplayRect, const MDFN_Rect *LineWidths,
|
||||
const int16 *SoundBuf, const int32 SoundBufSize);
|
||||
private:
|
||||
|
||||
void w8(uint8 val);
|
||||
void w16(uint16 val);
|
||||
void w32(uint32 val);
|
||||
void w32s(const char *str);
|
||||
void w64s(const char *str);
|
||||
void w64(uint64 val);
|
||||
uint32 wps(const char *str, uint32 max_len);
|
||||
void atom_begin(uint32 type, bool small_atom = true);
|
||||
void atom_begin(const char *type, bool small_atom = true);
|
||||
void atom_end(void);
|
||||
|
||||
void vardata_begin(void);
|
||||
void vardata_end(void);
|
||||
|
||||
void Write_ftyp(void);
|
||||
void Write_mvhd(void);
|
||||
void Write_tkhd(void);
|
||||
void Write_stsd(void);
|
||||
void Write_stts(void);
|
||||
void Write_stsc(void);
|
||||
void Write_stsz(void);
|
||||
void Write_co64(void);
|
||||
void Write_stco(void);
|
||||
void Write_stbl(void);
|
||||
void Write_mdhd(void);
|
||||
void Write_smhd(void);
|
||||
void Write_vmhd(void);
|
||||
void Write_hdlr(const char *str, const char *comp_name);
|
||||
void Write_dinf(void);
|
||||
void Write_minf(void);
|
||||
void Write_mdia(void);
|
||||
void Write_edts(void);
|
||||
void Write_trak(void);
|
||||
void Write_udta(void);
|
||||
void Write_moov(void);
|
||||
|
||||
|
||||
FileWrapper qtfile;
|
||||
|
||||
std::vector<uint8> RawVideoBuffer;
|
||||
std::vector<uint8> CompressedVideoBuffer;
|
||||
|
||||
std::list<bool> atom_smalls;
|
||||
std::list<int64> atom_foffsets;
|
||||
std::list<int64> vardata_foffsets;
|
||||
bool OnAudioTrack; // Yay spaghetti code power.
|
||||
|
||||
struct QTChunk
|
||||
{
|
||||
int64 video_foffset;
|
||||
int64 video_byte_size;
|
||||
|
||||
int64 audio_foffset;
|
||||
int64 audio_byte_size;
|
||||
};
|
||||
|
||||
int VideoCodec;
|
||||
uint32 QTVideoWidth;
|
||||
uint32 QTVideoHeight;
|
||||
uint32 SoundRate;
|
||||
uint32 SoundChan;
|
||||
uint32 A;
|
||||
uint32 D;
|
||||
|
||||
uint32 CreationTS;
|
||||
uint32 ModificationTS;
|
||||
|
||||
std::vector<QTChunk> QTChunks;
|
||||
uint64 SoundFramesWritten;
|
||||
|
||||
bool Finished;
|
||||
};
|
||||
|
||||
#endif
|
@ -27,13 +27,9 @@
|
||||
#include "driver.h"
|
||||
#include "general.h"
|
||||
#include "state.h"
|
||||
#include "movie.h"
|
||||
#include "netplay.h"
|
||||
#include "video.h"
|
||||
#include "video/resize.h"
|
||||
|
||||
static int SaveStateStatus[10];
|
||||
|
||||
#define RLSB MDFNSTATE_RLSB //0x80000000
|
||||
|
||||
int32 smem_read(StateMem *st, void *buffer, uint32 len)
|
||||
@ -461,7 +457,6 @@ static int ReadStateChunk(StateMem *st, SFORMAT *sf, int size, int data_only)
|
||||
}
|
||||
|
||||
static int CurrentState = 0;
|
||||
static int RecentlySavedState = -1;
|
||||
|
||||
/* This function is called by the game driver(NES, GB, GBA) to save a state. */
|
||||
int MDFNSS_StateAction(StateMem *st, int load, int data_only, std::vector <SSDescriptor> §ions)
|
||||
@ -600,41 +595,6 @@ int MDFNSS_SaveSM(StateMem *st, int wantpreview_and_ts, int data_only, const MDF
|
||||
smem_write(st, header, 32);
|
||||
}
|
||||
|
||||
if(wantpreview_and_ts)
|
||||
{
|
||||
uint8 *previewbuffer = (uint8 *)malloc(4 * neowidth * neoheight);
|
||||
MDFN_Surface *dest_surface = new MDFN_Surface((uint32 *)previewbuffer, neowidth, neoheight, neowidth, surface->format);
|
||||
MDFN_Rect dest_rect;
|
||||
|
||||
dest_rect.x = 0;
|
||||
dest_rect.y = 0;
|
||||
dest_rect.w = neowidth;
|
||||
dest_rect.h = neoheight;
|
||||
|
||||
MDFN_ResizeSurface(surface, DisplayRect, (LineWidths[0].w != ~0) ? LineWidths : NULL, dest_surface, &dest_rect);
|
||||
|
||||
{
|
||||
uint32 a, b = 0;
|
||||
for(a = 0; a < neowidth * neoheight * 4; a+=4)
|
||||
{
|
||||
uint32 c = *(uint32 *)&previewbuffer[a];
|
||||
int nr, ng, nb;
|
||||
|
||||
surface->DecodeColor(c, nr, ng, nb);
|
||||
|
||||
previewbuffer[b + 0] = nr;
|
||||
previewbuffer[b + 1] = ng;
|
||||
previewbuffer[b + 2] = nb;
|
||||
b += 3;
|
||||
}
|
||||
}
|
||||
|
||||
smem_write(st, previewbuffer, 3 * neowidth * neoheight);
|
||||
|
||||
free(previewbuffer);
|
||||
delete dest_surface;
|
||||
}
|
||||
|
||||
// State rewinding code path hack, FIXME
|
||||
if(data_only)
|
||||
{
|
||||
@ -678,7 +638,6 @@ int MDFNSS_Save(const char *fname, const char *suffix, const MDFN_Surface *surfa
|
||||
|
||||
if(!MDFN_DumpToFile(fname ? fname : MDFN_MakeFName(MDFNMKF_STATE,CurrentState,suffix).c_str(), 6, st.data, st.len))
|
||||
{
|
||||
SaveStateStatus[CurrentState] = 0;
|
||||
free(st.data);
|
||||
|
||||
if(!fname && !suffix)
|
||||
@ -689,43 +648,12 @@ int MDFNSS_Save(const char *fname, const char *suffix, const MDFN_Surface *surfa
|
||||
|
||||
free(st.data);
|
||||
|
||||
SaveStateStatus[CurrentState] = 1;
|
||||
RecentlySavedState = CurrentState;
|
||||
|
||||
if(!fname && !suffix)
|
||||
MDFN_DispMessage(_("State %d saved."),CurrentState);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
// Convenience function for movie.cpp
|
||||
int MDFNSS_SaveFP(gzFile fp, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const MDFN_Rect *LineWidths)
|
||||
{
|
||||
StateMem st;
|
||||
|
||||
memset(&st, 0, sizeof(StateMem));
|
||||
|
||||
if(!MDFNSS_SaveSM(&st, (DisplayRect && LineWidths), 0, surface, DisplayRect, LineWidths))
|
||||
{
|
||||
if(st.data)
|
||||
free(st.data);
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(gzwrite(fp, st.data, st.len) != (int32)st.len)
|
||||
{
|
||||
if(st.data)
|
||||
free(st.data);
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(st.data)
|
||||
free(st.data);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int MDFNSS_LoadSM(StateMem *st, int haspreview, int data_only)
|
||||
{
|
||||
uint8 header[32];
|
||||
@ -765,478 +693,3 @@ int MDFNSS_LoadSM(StateMem *st, int haspreview, int data_only)
|
||||
return(MDFNGameInfo->StateAction(st, stateversion, data_only));
|
||||
}
|
||||
|
||||
int MDFNSS_LoadFP(gzFile fp)
|
||||
{
|
||||
uint8 header[32];
|
||||
StateMem st;
|
||||
|
||||
memset(&st, 0, sizeof(StateMem));
|
||||
|
||||
if(gzread(fp, header, 32) != 32)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
st.len = MDFN_de32lsb(header + 16 + 4);
|
||||
|
||||
if(st.len < 32)
|
||||
return(0);
|
||||
|
||||
if(!(st.data = (uint8 *)malloc(st.len)))
|
||||
return(0);
|
||||
|
||||
memcpy(st.data, header, 32);
|
||||
if(gzread(fp, st.data + 32, st.len - 32) != ((int32)st.len - 32))
|
||||
{
|
||||
free(st.data);
|
||||
return(0);
|
||||
}
|
||||
if(!MDFNSS_LoadSM(&st, 1, 0))
|
||||
{
|
||||
free(st.data);
|
||||
return(0);
|
||||
}
|
||||
free(st.data);
|
||||
return(1);
|
||||
}
|
||||
|
||||
int MDFNSS_Load(const char *fname, const char *suffix)
|
||||
{
|
||||
gzFile st;
|
||||
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
{
|
||||
MDFN_DispMessage(_("Module \"%s\" doesn't support save states."), MDFNGameInfo->shortname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(fname)
|
||||
st=gzopen(fname, "rb");
|
||||
else
|
||||
{
|
||||
st=gzopen(MDFN_MakeFName(MDFNMKF_STATE,CurrentState,suffix).c_str(),"rb");
|
||||
}
|
||||
|
||||
if(st == NULL)
|
||||
{
|
||||
if(!fname && !suffix)
|
||||
{
|
||||
MDFN_DispMessage(_("State %d load error."),CurrentState);
|
||||
SaveStateStatus[CurrentState]=0;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(MDFNSS_LoadFP(st))
|
||||
{
|
||||
if(!fname && !suffix)
|
||||
{
|
||||
SaveStateStatus[CurrentState]=1;
|
||||
MDFN_DispMessage(_("State %d loaded."),CurrentState);
|
||||
SaveStateStatus[CurrentState]=1;
|
||||
}
|
||||
gzclose(st);
|
||||
return(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveStateStatus[CurrentState]=1;
|
||||
MDFN_DispMessage(_("State %d read error!"),CurrentState);
|
||||
gzclose(st);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MDFNSS_CheckStates(void)
|
||||
{
|
||||
time_t last_time = 0;
|
||||
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
return;
|
||||
|
||||
|
||||
for(int ssel = 0; ssel < 10; ssel++)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
||||
SaveStateStatus[ssel] = 0;
|
||||
//printf("%s\n", MDFN_MakeFName(MDFNMKF_STATE, ssel, 0).c_str());
|
||||
if(stat(MDFN_MakeFName(MDFNMKF_STATE, ssel, 0).c_str(), &stat_buf) == 0)
|
||||
{
|
||||
SaveStateStatus[ssel] = 1;
|
||||
if(stat_buf.st_mtime > last_time)
|
||||
{
|
||||
RecentlySavedState = ssel;
|
||||
last_time = stat_buf.st_mtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentState = 0;
|
||||
MDFND_SetStateStatus(NULL);
|
||||
}
|
||||
|
||||
void MDFNSS_GetStateInfo(const char *filename, StateStatusStruct *status)
|
||||
{
|
||||
gzFile fp;
|
||||
uint32 StateShowPBWidth;
|
||||
uint32 StateShowPBHeight;
|
||||
uint8 *previewbuffer = NULL;
|
||||
|
||||
fp = gzopen(filename, "rb");
|
||||
if(fp)
|
||||
{
|
||||
uint8 header[32];
|
||||
|
||||
gzread(fp, header, 32);
|
||||
uint32 width = MDFN_de32lsb(header + 24);
|
||||
uint32 height = MDFN_de32lsb(header + 28);
|
||||
|
||||
if(width > 1024) width = 1024;
|
||||
if(height > 1024) height = 1024;
|
||||
|
||||
if(!(previewbuffer = (uint8 *)MDFN_malloc(3 * width * height, _("Save state preview buffer"))))
|
||||
{
|
||||
StateShowPBWidth = 0;
|
||||
StateShowPBHeight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gzread(fp, previewbuffer, 3 * width * height);
|
||||
|
||||
StateShowPBWidth = width;
|
||||
StateShowPBHeight = height;
|
||||
}
|
||||
gzclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateShowPBWidth = MDFNGameInfo->nominal_width;
|
||||
StateShowPBHeight = MDFNGameInfo->nominal_height;
|
||||
}
|
||||
|
||||
status->gfx = previewbuffer;
|
||||
status->w = StateShowPBWidth;
|
||||
status->h = StateShowPBHeight;
|
||||
}
|
||||
|
||||
void MDFNI_SelectState(int w)
|
||||
{
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
return;
|
||||
|
||||
|
||||
if(w == -1)
|
||||
{
|
||||
MDFND_SetStateStatus(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if(w == 666 + 1)
|
||||
CurrentState = (CurrentState + 1) % 10;
|
||||
else if(w == 666 - 1)
|
||||
{
|
||||
CurrentState--;
|
||||
|
||||
if(CurrentState < 0 || CurrentState > 9)
|
||||
CurrentState = 9;
|
||||
}
|
||||
else
|
||||
CurrentState = w;
|
||||
|
||||
MDFN_ResetMessages();
|
||||
|
||||
StateStatusStruct *status = (StateStatusStruct*)MDFN_calloc(1, sizeof(StateStatusStruct), _("Save state status"));
|
||||
|
||||
memcpy(status->status, SaveStateStatus, 10 * sizeof(int));
|
||||
|
||||
status->current = CurrentState;
|
||||
status->recently_saved = RecentlySavedState;
|
||||
|
||||
MDFNSS_GetStateInfo(MDFN_MakeFName(MDFNMKF_STATE,CurrentState,NULL).c_str(), status);
|
||||
MDFND_SetStateStatus(status);
|
||||
}
|
||||
|
||||
void MDFNI_SaveState(const char *fname, const char *suffix, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const MDFN_Rect *LineWidths)
|
||||
{
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
return;
|
||||
|
||||
MDFND_SetStateStatus(NULL);
|
||||
MDFNSS_Save(fname, suffix, surface, DisplayRect, LineWidths);
|
||||
}
|
||||
|
||||
void MDFNI_LoadState(const char *fname, const char *suffix)
|
||||
{
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
return;
|
||||
|
||||
MDFND_SetStateStatus(NULL);
|
||||
|
||||
/* For network play and movies, be load the state locally, and then save the state to a temporary buffer,
|
||||
and send or record that. This ensures that if an older state is loaded that is missing some
|
||||
information expected in newer save states, desynchronization won't occur(at least not
|
||||
from this ;)).
|
||||
*/
|
||||
if(MDFNSS_Load(fname, suffix))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#include "compress/minilzo.h"
|
||||
#include "compress/quicklz.h"
|
||||
#include "compress/blz.h"
|
||||
|
||||
static union
|
||||
{
|
||||
char qlz_scratch_compress[/*QLZ_*/SCRATCH_COMPRESS];
|
||||
char qlz_scratch_decompress[/*QLZ_*/SCRATCH_DECOMPRESS];
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SRW_COMPRESSOR_MINILZO = 0,
|
||||
SRW_COMPRESSOR_QUICKLZ,
|
||||
SRW_COMPRESSOR_BLZ
|
||||
};
|
||||
|
||||
struct StateMemPacket
|
||||
{
|
||||
uint8 *data;
|
||||
uint32 compressed_len;
|
||||
uint32 uncompressed_len;
|
||||
|
||||
StateMem MovieLove;
|
||||
};
|
||||
|
||||
static int SRW_NUM = 600;
|
||||
static int SRWCompressor;
|
||||
static int EvilEnabled = 0;
|
||||
static StateMemPacket *bcs;
|
||||
static int32 bcspos;
|
||||
|
||||
void MDFN_StateEvilBegin(void)
|
||||
{
|
||||
int x;
|
||||
std::string srwcompstring;
|
||||
|
||||
|
||||
if(!EvilEnabled)
|
||||
return;
|
||||
|
||||
SRW_NUM = MDFN_GetSettingUI("srwframes");
|
||||
|
||||
SRWCompressor = SRW_COMPRESSOR_MINILZO;
|
||||
srwcompstring = MDFN_GetSettingS("srwcompressor");
|
||||
|
||||
if(srwcompstring == "minilzo")
|
||||
SRWCompressor = SRW_COMPRESSOR_MINILZO;
|
||||
else if(srwcompstring == "quicklz")
|
||||
SRWCompressor = SRW_COMPRESSOR_QUICKLZ;
|
||||
else if(srwcompstring == "blz")
|
||||
SRWCompressor = SRW_COMPRESSOR_BLZ;
|
||||
|
||||
bcs = (StateMemPacket *)calloc(SRW_NUM, sizeof(StateMemPacket));
|
||||
bcspos = 0;
|
||||
|
||||
for(x=0;x<SRW_NUM;x++)
|
||||
{
|
||||
bcs[x].data = NULL;
|
||||
bcs[x].compressed_len = 0;
|
||||
bcs[x].uncompressed_len = 0;
|
||||
memset(&bcs[x].MovieLove, 0, sizeof(StateMem));
|
||||
}
|
||||
}
|
||||
|
||||
bool MDFN_StateEvilIsRunning(void)
|
||||
{
|
||||
return(EvilEnabled);
|
||||
}
|
||||
|
||||
void MDFN_StateEvilEnd(void)
|
||||
{
|
||||
int x;
|
||||
|
||||
if(!EvilEnabled)
|
||||
return;
|
||||
|
||||
if(bcs)
|
||||
{
|
||||
for(x = 0;x < SRW_NUM; x++)
|
||||
{
|
||||
|
||||
if(bcs[x].data)
|
||||
free(bcs[x].data);
|
||||
bcs[x].data = NULL;
|
||||
bcs[x].compressed_len = 0;
|
||||
}
|
||||
free(bcs);
|
||||
}
|
||||
}
|
||||
|
||||
void MDFN_StateEvilFlushMovieLove(void)
|
||||
{
|
||||
int bahpos = (bcspos + 1) % SRW_NUM;
|
||||
for(int x = 0; x < SRW_NUM; x++)
|
||||
{
|
||||
if(bcs[bahpos].MovieLove.data)
|
||||
{
|
||||
free(bcs[bahpos].MovieLove.data);
|
||||
bcs[bahpos].MovieLove.data = NULL;
|
||||
}
|
||||
bahpos = (bahpos + 1) % SRW_NUM;
|
||||
}
|
||||
}
|
||||
|
||||
int MDFN_StateEvil(int rewind)
|
||||
{
|
||||
if(!EvilEnabled)
|
||||
return(0);
|
||||
|
||||
if(rewind)
|
||||
{
|
||||
int32 next_bcspos = bcspos;
|
||||
bool NeedDataFlush = FALSE;
|
||||
|
||||
bcspos--;
|
||||
if(bcspos < 0) bcspos += SRW_NUM;
|
||||
|
||||
if(!bcs[bcspos].data)
|
||||
bcspos = (bcspos + 1) % SRW_NUM;
|
||||
else
|
||||
NeedDataFlush = TRUE;
|
||||
|
||||
if(bcs[bcspos].compressed_len)
|
||||
{
|
||||
uint8 *tmp_buf;
|
||||
lzo_uint dst_len = bcs[bcspos].uncompressed_len;
|
||||
|
||||
tmp_buf = (uint8 *)malloc(bcs[bcspos].uncompressed_len);
|
||||
|
||||
if(SRWCompressor == SRW_COMPRESSOR_QUICKLZ)
|
||||
dst_len = qlz_decompress((char*)bcs[bcspos].data, tmp_buf, qlz_scratch_decompress);
|
||||
else if(SRWCompressor == SRW_COMPRESSOR_MINILZO)
|
||||
lzo1x_decompress(bcs[bcspos].data, bcs[bcspos].compressed_len, tmp_buf, &dst_len, NULL);
|
||||
else if(SRWCompressor == SRW_COMPRESSOR_BLZ)
|
||||
{
|
||||
dst_len = blz_unpack(bcs[bcspos].data, tmp_buf);
|
||||
}
|
||||
for(uint32 x = 0; x < bcs[bcspos].uncompressed_len && x < bcs[next_bcspos].uncompressed_len; x++)
|
||||
tmp_buf[x] ^= bcs[next_bcspos].data[x];
|
||||
|
||||
free(bcs[bcspos].data);
|
||||
bcs[bcspos].data = tmp_buf;
|
||||
bcs[bcspos].compressed_len = 0;
|
||||
}
|
||||
|
||||
if(NeedDataFlush)
|
||||
{
|
||||
if(bcs[next_bcspos].MovieLove.data)
|
||||
{
|
||||
free(bcs[next_bcspos].MovieLove.data);
|
||||
bcs[next_bcspos].MovieLove.data = NULL;
|
||||
}
|
||||
free(bcs[next_bcspos].data);
|
||||
bcs[next_bcspos].data = NULL;
|
||||
bcs[next_bcspos].compressed_len = 0;
|
||||
bcs[next_bcspos].uncompressed_len = 0;
|
||||
}
|
||||
|
||||
if(bcs[bcspos].uncompressed_len)
|
||||
{
|
||||
StateMem sm;
|
||||
|
||||
sm.data = bcs[bcspos].data;
|
||||
sm.loc = 0;
|
||||
sm.initial_malloc = 0;
|
||||
sm.malloced = sm.len = bcs[bcspos].uncompressed_len;
|
||||
|
||||
MDFNSS_LoadSM(&sm, 0, 1);
|
||||
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StateMem sm;
|
||||
int32 prev_bcspos = bcspos;
|
||||
|
||||
bcspos = (bcspos + 1) % SRW_NUM;
|
||||
|
||||
if(bcs[bcspos].data)
|
||||
{
|
||||
free(bcs[bcspos].data);
|
||||
bcs[bcspos].data = NULL;
|
||||
}
|
||||
if(bcs[bcspos].MovieLove.data)
|
||||
{
|
||||
free(bcs[bcspos].MovieLove.data);
|
||||
bcs[bcspos].MovieLove.data = NULL;
|
||||
}
|
||||
|
||||
memset(&sm, 0, sizeof(sm));
|
||||
|
||||
MDFNSS_SaveSM(&sm, 0, 1);
|
||||
|
||||
bcs[bcspos].data = sm.data;
|
||||
bcs[bcspos].compressed_len = 0;
|
||||
bcs[bcspos].uncompressed_len = sm.len;
|
||||
|
||||
// Compress the previous save state.
|
||||
if(bcs[prev_bcspos].data)
|
||||
{
|
||||
for(uint32 x = 0; x < bcs[prev_bcspos].uncompressed_len && x < sm.len; x++)
|
||||
bcs[prev_bcspos].data[x] ^= sm.data[x];
|
||||
|
||||
if(SRWCompressor == SRW_COMPRESSOR_QUICKLZ)
|
||||
{
|
||||
uint32 dst_len;
|
||||
uint8 *tmp_buf = (uint8 *)malloc(bcs[prev_bcspos].uncompressed_len + 400);
|
||||
|
||||
dst_len = qlz_compress(bcs[prev_bcspos].data, (char*)tmp_buf, bcs[prev_bcspos].uncompressed_len, qlz_scratch_compress);
|
||||
|
||||
free(bcs[prev_bcspos].data);
|
||||
bcs[prev_bcspos].data = (uint8 *)realloc(tmp_buf, dst_len);
|
||||
bcs[prev_bcspos].compressed_len = dst_len;
|
||||
}
|
||||
else if(SRWCompressor == SRW_COMPRESSOR_MINILZO)
|
||||
{
|
||||
uint8 workmem[LZO1X_1_MEM_COMPRESS];
|
||||
uint8 * tmp_buf = (uint8 *)malloc((size_t)(1.10 * bcs[prev_bcspos].uncompressed_len));
|
||||
lzo_uint dst_len = (lzo_uint)(1.10 * bcs[prev_bcspos].uncompressed_len);
|
||||
|
||||
lzo1x_1_compress(bcs[prev_bcspos].data, bcs[prev_bcspos].uncompressed_len, tmp_buf, &dst_len, workmem);
|
||||
|
||||
free(bcs[prev_bcspos].data);
|
||||
bcs[prev_bcspos].data = (uint8 *)realloc(tmp_buf, dst_len);
|
||||
bcs[prev_bcspos].compressed_len = dst_len;
|
||||
}
|
||||
else if(SRWCompressor == SRW_COMPRESSOR_BLZ)
|
||||
{
|
||||
blz_pack_t workmem;
|
||||
|
||||
uint8 * tmp_buf = (uint8 *)malloc((size_t)(bcs[prev_bcspos].uncompressed_len + blz_pack_extra));
|
||||
uint32 dst_len = bcs[prev_bcspos].uncompressed_len + blz_pack_extra;
|
||||
|
||||
dst_len = blz_pack(bcs[prev_bcspos].data, bcs[prev_bcspos].uncompressed_len, tmp_buf, &workmem);
|
||||
|
||||
free(bcs[prev_bcspos].data);
|
||||
bcs[prev_bcspos].data = (uint8 *)realloc(tmp_buf, dst_len);
|
||||
bcs[prev_bcspos].compressed_len = dst_len;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
void MDFNI_EnableStateRewind(int enable)
|
||||
{
|
||||
if(!MDFNGameInfo->StateAction)
|
||||
return;
|
||||
|
||||
MDFN_StateEvilEnd();
|
||||
|
||||
EvilEnabled = enable;
|
||||
|
||||
MDFN_StateEvilBegin();
|
||||
}
|
||||
|
@ -1,656 +0,0 @@
|
||||
// DO NOT REMOVE/DISABLE THESE MATH AND COMPILER SANITY TESTS. THEY EXIST FOR A REASON.
|
||||
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// We really don't want NDEBUG defined ;)
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include "mednafen.h"
|
||||
#include "lepacker.h"
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
#define FATALME { printf("Math test failed: %s:%d\n", __FILE__, __LINE__); fprintf(stderr, "Math test failed: %s:%d\n", __FILE__, __LINE__); return(0); }
|
||||
|
||||
// Don't define this static, and don't define it const. We want these tests to be done at run time, not compile time(although maybe we should do both...).
|
||||
typedef struct
|
||||
{
|
||||
int bits;
|
||||
uint32 negative_one;
|
||||
uint32 mostneg;
|
||||
int32 mostnegresult;
|
||||
} MathTestEntry;
|
||||
|
||||
#define ADD_MTE(_bits) { _bits, ((uint32)1 << _bits) - 1, (uint32)1 << (_bits - 1), (int32)(0 - ((uint32)1 << (_bits - 1))) }
|
||||
|
||||
MathTestEntry math_test_vals[] =
|
||||
{
|
||||
{ 9, 0x01FF, 0x0100, -256 },
|
||||
{ 10, 0x03FF, 0x0200, -512 },
|
||||
{ 11, 0x07FF, 0x0400, -1024 },
|
||||
{ 12, 0x0FFF, 0x0800, -2048 },
|
||||
{ 13, 0x1FFF, 0x1000, -4096 },
|
||||
{ 14, 0x3FFF, 0x2000, -8192 },
|
||||
{ 15, 0x7FFF, 0x4000, -16384 },
|
||||
|
||||
ADD_MTE(17),
|
||||
ADD_MTE(18),
|
||||
ADD_MTE(19),
|
||||
ADD_MTE(20),
|
||||
ADD_MTE(21),
|
||||
ADD_MTE(22),
|
||||
ADD_MTE(23),
|
||||
ADD_MTE(24),
|
||||
ADD_MTE(25),
|
||||
ADD_MTE(26),
|
||||
ADD_MTE(27),
|
||||
ADD_MTE(28),
|
||||
ADD_MTE(29),
|
||||
ADD_MTE(30),
|
||||
ADD_MTE(31),
|
||||
|
||||
{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static bool DoSizeofTests(void)
|
||||
{
|
||||
const int SizePairs[][2] =
|
||||
{
|
||||
{ sizeof(uint8), 1 },
|
||||
{ sizeof(int8), 1 },
|
||||
|
||||
{ sizeof(uint16), 2 },
|
||||
{ sizeof(int16), 2 },
|
||||
|
||||
{ sizeof(uint32), 4 },
|
||||
{ sizeof(int32), 4 },
|
||||
|
||||
{ sizeof(uint64), 8 },
|
||||
{ sizeof(int64), 8 },
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
int i = -1;
|
||||
|
||||
while(SizePairs[++i][0])
|
||||
{
|
||||
if(SizePairs[i][0] != SizePairs[i][1])
|
||||
FATALME;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static void AntiNSOBugTest_Sub1_a(int *array) NO_INLINE;
|
||||
static void AntiNSOBugTest_Sub1_a(int *array)
|
||||
{
|
||||
for(int value = 0; value < 127; value++)
|
||||
array[value] += (int8)value * 15;
|
||||
}
|
||||
|
||||
static void AntiNSOBugTest_Sub1_b(int *array) NO_INLINE;
|
||||
static void AntiNSOBugTest_Sub1_b(int *array)
|
||||
{
|
||||
for(int value = 127; value < 256; value++)
|
||||
array[value] += (int8)value * 15;
|
||||
}
|
||||
|
||||
static void AntiNSOBugTest_Sub2(int *array) NO_INLINE;
|
||||
static void AntiNSOBugTest_Sub2(int *array)
|
||||
{
|
||||
for(int value = 0; value < 256; value++)
|
||||
array[value] += (int8)value * 15;
|
||||
}
|
||||
|
||||
static void AntiNSOBugTest_Sub3(int *array) NO_INLINE;
|
||||
static void AntiNSOBugTest_Sub3(int *array)
|
||||
{
|
||||
for(int value = 0; value < 256; value++)
|
||||
{
|
||||
if(value >= 128)
|
||||
array[value] = (value - 256) * 15;
|
||||
else
|
||||
array[value] = value * 15;
|
||||
}
|
||||
}
|
||||
|
||||
static bool DoAntiNSOBugTest(void)
|
||||
{
|
||||
int array1[256], array2[256], array3[256];
|
||||
|
||||
memset(array1, 0, sizeof(array1));
|
||||
memset(array2, 0, sizeof(array2));
|
||||
memset(array3, 0, sizeof(array3));
|
||||
|
||||
AntiNSOBugTest_Sub1_a(array1);
|
||||
AntiNSOBugTest_Sub1_b(array1);
|
||||
AntiNSOBugTest_Sub2(array2);
|
||||
AntiNSOBugTest_Sub3(array3);
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
if((array1[i] != array2[i]) || (array2[i] != array3[i]))
|
||||
{
|
||||
printf("%d %d %d %d\n", i, array1[i], array2[i], array3[i]);
|
||||
FATALME;
|
||||
}
|
||||
}
|
||||
//for(int value = 0; value < 256; value++)
|
||||
// printf("%d, %d\n", (int8)value, ((int8)value) * 15);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
bool DoLEPackerTest(void)
|
||||
{
|
||||
MDFN::LEPacker mizer;
|
||||
static const uint8 correct_result[24] = { 0xed, 0xfe, 0xed, 0xde, 0xaa, 0xca, 0xef, 0xbe, 0xbe, 0xba, 0xfe, 0xca, 0xad, 0xde, 0x01, 0x9a, 0x0c, 0xa7, 0xff, 0x00, 0xff, 0xff, 0x55, 0x7f };
|
||||
|
||||
uint64 u64_test = 0xDEADCAFEBABEBEEFULL;
|
||||
uint32 u32_test = 0xDEEDFEED;
|
||||
uint16 u16_test = 0xCAAA;
|
||||
uint8 u8_test = 0x55;
|
||||
int32 s32_test = -5829478;
|
||||
int16 s16_test = -1;
|
||||
int8 s8_test = 127;
|
||||
|
||||
bool bool_test0 = 0;
|
||||
bool bool_test1 = 1;
|
||||
|
||||
mizer ^ u32_test;
|
||||
mizer ^ u16_test;
|
||||
mizer ^ u64_test;
|
||||
mizer ^ bool_test1;
|
||||
mizer ^ s32_test;
|
||||
mizer ^ bool_test0;
|
||||
mizer ^ s16_test;
|
||||
mizer ^ u8_test;
|
||||
mizer ^ s8_test;
|
||||
|
||||
if(mizer.size() != 24)
|
||||
{
|
||||
printf("Test failed: LEPacker data incorrect size.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < mizer.size(); i++)
|
||||
if(mizer[i] != correct_result[i])
|
||||
{
|
||||
printf("Test failed: LEPacker packed data incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
u64_test = 0;
|
||||
u32_test = 0;
|
||||
u16_test = 0;
|
||||
u8_test = 0;
|
||||
s32_test = 0;
|
||||
s16_test = 0;
|
||||
s8_test = 0;
|
||||
|
||||
bool_test0 = 1;
|
||||
bool_test1 = 0;
|
||||
|
||||
mizer.set_read_mode(TRUE);
|
||||
|
||||
mizer ^ u32_test;
|
||||
mizer ^ u16_test;
|
||||
mizer ^ u64_test;
|
||||
mizer ^ bool_test1;
|
||||
mizer ^ s32_test;
|
||||
mizer ^ bool_test0;
|
||||
mizer ^ s16_test;
|
||||
mizer ^ u8_test;
|
||||
mizer ^ s8_test;
|
||||
|
||||
|
||||
if(u32_test != 0xDEEDFEED)
|
||||
{
|
||||
printf("Test failed: LEPacker u32 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(u16_test != 0xCAAA)
|
||||
{
|
||||
printf("Test failed: LEPacker u16 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(u64_test != 0xDEADCAFEBABEBEEFULL)
|
||||
{
|
||||
printf("%16llx\n", (unsigned long long)u64_test);
|
||||
printf("Test failed: LEPacker u64 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(u8_test != 0x55)
|
||||
{
|
||||
printf("Test failed: LEPacker u8 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(s32_test != -5829478)
|
||||
{
|
||||
printf("Test failed: LEPacker s32 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(s16_test != -1)
|
||||
{
|
||||
printf("Test failed: LEPacker s16 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(s8_test != 127)
|
||||
{
|
||||
printf("Test failed: LEPacker s8 unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(bool_test0 != 0)
|
||||
{
|
||||
printf("Test failed: LEPacker bool unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(bool_test1 != 1)
|
||||
{
|
||||
printf("Test failed: LEPacker bool unpacking incorrect.\n");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
struct MathTestTSOEntry
|
||||
{
|
||||
int32 a;
|
||||
int32 b;
|
||||
};
|
||||
|
||||
// Don't declare as static(though whopr might mess it up anyway)
|
||||
MathTestTSOEntry MathTestTSOTests[] =
|
||||
{
|
||||
{ 0x7FFFFFFF, 2 },
|
||||
{ 0x7FFFFFFE, 0x7FFFFFFF },
|
||||
{ 0x7FFFFFFF, 0x7FFFFFFF },
|
||||
{ 0x7FFFFFFE, 0x7FFFFFFE },
|
||||
};
|
||||
|
||||
static void TestSignedOverflow(void)
|
||||
{
|
||||
for(unsigned int i = 0; i < sizeof(MathTestTSOTests) / sizeof(MathTestTSOEntry); i++)
|
||||
{
|
||||
int32 a = MathTestTSOTests[i].a;
|
||||
int32 b = MathTestTSOTests[i].b;
|
||||
|
||||
assert((a + b) < a && (a + b) < b);
|
||||
|
||||
assert((a + 0x7FFFFFFE) < a);
|
||||
assert((b + 0x7FFFFFFE) < b);
|
||||
|
||||
assert((a + 0x7FFFFFFF) < a);
|
||||
assert((b + 0x7FFFFFFF) < b);
|
||||
|
||||
assert((int32)(a + 0x80000000) < a);
|
||||
assert((int32)(b + 0x80000000) < b);
|
||||
|
||||
assert((int32)(a ^ 0x80000000) < a);
|
||||
assert((int32)(b ^ 0x80000000) < b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DoAlignmentChecks(void)
|
||||
{
|
||||
uint8 padding0[3];
|
||||
MDFN_ALIGN(16) uint8 aligned0[7];
|
||||
MDFN_ALIGN(4) uint8 aligned1[2];
|
||||
MDFN_ALIGN(16) uint32 aligned2[2];
|
||||
uint8 padding1[3];
|
||||
|
||||
static uint8 g_padding0[3];
|
||||
static MDFN_ALIGN(16) uint8 g_aligned0[7];
|
||||
static MDFN_ALIGN(4) uint8 g_aligned1[2];
|
||||
static MDFN_ALIGN(16) uint32 g_aligned2[2];
|
||||
static uint8 g_padding1[3];
|
||||
|
||||
// Make sure compiler doesn't removing padding vars
|
||||
assert((&padding0[1] - &padding0[0]) == 1);
|
||||
assert((&padding1[1] - &padding1[0]) == 1);
|
||||
assert((&g_padding0[1] - &g_padding0[0]) == 1);
|
||||
assert((&g_padding1[1] - &g_padding1[0]) == 1);
|
||||
|
||||
|
||||
assert( (((unsigned long long)&aligned0[0]) & 0xF) == 0);
|
||||
assert( (((unsigned long long)&aligned1[0]) & 0x3) == 0);
|
||||
assert( (((unsigned long long)&aligned2[0]) & 0xF) == 0);
|
||||
|
||||
assert(((uint8 *)&aligned0[1] - (uint8 *)&aligned0[0]) == 1);
|
||||
assert(((uint8 *)&aligned1[1] - (uint8 *)&aligned1[0]) == 1);
|
||||
assert(((uint8 *)&aligned2[1] - (uint8 *)&aligned2[0]) == 4);
|
||||
|
||||
|
||||
assert( (((unsigned long long)&g_aligned0[0]) & 0xF) == 0);
|
||||
assert( (((unsigned long long)&g_aligned1[0]) & 0x3) == 0);
|
||||
assert( (((unsigned long long)&g_aligned2[0]) & 0xF) == 0);
|
||||
|
||||
assert(((uint8 *)&g_aligned0[1] - (uint8 *)&g_aligned0[0]) == 1);
|
||||
assert(((uint8 *)&g_aligned1[1] - (uint8 *)&g_aligned1[0]) == 1);
|
||||
assert(((uint8 *)&g_aligned2[1] - (uint8 *)&g_aligned2[0]) == 4);
|
||||
}
|
||||
|
||||
#include "masmem.h"
|
||||
|
||||
static void QuickEndianRBOTest(void)
|
||||
{
|
||||
uint32 test[2] = { 0xDEADBEEF, 0xCAFEBABE };
|
||||
uint32 test2 = { 0xD00FD00F };
|
||||
|
||||
assert(LoadU32_RBO(&test[0]) == 0xEFBEADDE);
|
||||
StoreU32_RBO(&test[1], 0x12341235);
|
||||
assert(test[1] == 0x35123412);
|
||||
assert(LoadU32_RBO(&test[1]) == 0x12341235);
|
||||
assert(LoadU32_RBO(&test2) == 0x0FD00FD0);
|
||||
}
|
||||
|
||||
// don't make this static, and don't make it local scope. Whole-program optimization might defeat the purpose of this, though...
|
||||
unsigned int mdfn_shifty_test[4] =
|
||||
{
|
||||
0, 8, 16, 32
|
||||
};
|
||||
|
||||
|
||||
#include "general.h"
|
||||
|
||||
bool MDFN_RunMathTests(void)
|
||||
{
|
||||
MathTestEntry *itoo = math_test_vals;
|
||||
|
||||
if(!DoSizeofTests())
|
||||
return(0);
|
||||
|
||||
// Make sure the "char" type is signed(pass -fsigned-char to gcc). New code in Mednafen shouldn't be written with the
|
||||
// assumption that "char" is signed, but there likely is at least some code that does.
|
||||
{
|
||||
char tmp = 255;
|
||||
assert(tmp < 0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO(except for 32-bit >> 32 test)
|
||||
{
|
||||
uint8 test_cow8 = (uint8)0xFF >> mdfn_shifty_test[1];
|
||||
uint16 test_cow16 = (uint16)0xFFFF >> mdfn_shifty_test[2];
|
||||
uint32 test_cow32 = (uint32)0xFFFFFFFF >> mdfn_shifty_test[3];
|
||||
uint32 test_cow32_2 = (uint32)0xFFFFFFFF >> mdfn_shifty_test[0];
|
||||
|
||||
printf("%08x\n", test_cow32);
|
||||
|
||||
assert(test_cow8 == 0);
|
||||
assert(test_cow16 == 0);
|
||||
assert(test_cow32 == 0);
|
||||
assert(test_cow32_2 == 0xFFFFFFFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
int32 meow;
|
||||
|
||||
meow = 1;
|
||||
meow >>= 1;
|
||||
assert(meow == 0);
|
||||
|
||||
meow = 5;
|
||||
meow >>= 1;
|
||||
assert(meow == 2);
|
||||
|
||||
meow = -1;
|
||||
meow >>= 1;
|
||||
assert(meow == -1);
|
||||
|
||||
meow = -5;
|
||||
meow >>= 1;
|
||||
assert(meow == -3);
|
||||
|
||||
meow = 1;
|
||||
meow /= 2;
|
||||
assert(meow == 0);
|
||||
|
||||
meow = 5;
|
||||
meow /= 2;
|
||||
assert(meow == 2);
|
||||
|
||||
meow = -1;
|
||||
meow /= 2;
|
||||
assert(meow == 0);
|
||||
|
||||
meow = -5;
|
||||
meow /= 2;
|
||||
assert(meow == -2);
|
||||
|
||||
meow = -5;
|
||||
meow = (int32)(meow + ((uint32)meow >> 31)) >> 1;
|
||||
assert(meow == -2);
|
||||
|
||||
#if 0
|
||||
meow = 1 << 30;
|
||||
meow <<= 1;
|
||||
assert(meow == -2147483648);
|
||||
|
||||
meow = 1 << 31;
|
||||
meow <<= 1;
|
||||
assert(meow == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// New tests added May 22, 2010 to detect MSVC compiler(and possibly other compilers) bad code generation.
|
||||
{
|
||||
uint32 test_tab[4] = { 0x2000 | 0x1000, 0x2000, 0x1000, 0x0000 };
|
||||
const uint32 result_tab[4][2] = { { 0xE, 0x7 }, { 0xE, 0x0 }, { 0x0, 0x7 }, { 0x0, 0x0 } };
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
uint32 hflip_xor;
|
||||
uint32 vflip_xor;
|
||||
uint32 bgsc;
|
||||
|
||||
bgsc = test_tab[i];
|
||||
|
||||
hflip_xor = ((int32)(bgsc << 18) >> 30) & 0xE;
|
||||
vflip_xor = ((int32)(bgsc << 19) >> 31) & 0x7;
|
||||
|
||||
assert(hflip_xor == result_tab[i][0]);
|
||||
assert(vflip_xor == result_tab[i][1]);
|
||||
|
||||
//printf("%d %d\n", hflip_xor, result_tab[i][0]);
|
||||
//printf("%d %d\n", vflip_xor, result_tab[i][1]);
|
||||
}
|
||||
|
||||
uint32 lfsr = 1;
|
||||
|
||||
// quick and dirty RNG(to also test non-constant-expression evaluation, at least until compilers are extremely advanced :b)
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
int feedback = ((lfsr >> 7) & 1) ^ ((lfsr >> 14) & 1);
|
||||
lfsr = ((lfsr << 1) & 0x7FFF) | feedback;
|
||||
|
||||
uint32 hflip_xor;
|
||||
uint32 vflip_xor;
|
||||
uint32 hflip_xor_alt;
|
||||
uint32 vflip_xor_alt;
|
||||
uint32 bgsc;
|
||||
|
||||
bgsc = lfsr;
|
||||
|
||||
hflip_xor = ((int32)(bgsc << 18) >> 30) & 0xE;
|
||||
vflip_xor = ((int32)(bgsc << 19) >> 31) & 0x7;
|
||||
|
||||
hflip_xor_alt = bgsc & 0x2000 ? 0xE : 0;
|
||||
vflip_xor_alt = bgsc & 0x1000 ? 0x7 : 0;
|
||||
|
||||
assert(hflip_xor == hflip_xor_alt);
|
||||
assert(vflip_xor == vflip_xor_alt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DoAlignmentChecks();
|
||||
TestSignedOverflow();
|
||||
|
||||
if(sign_9_to_s16(itoo->negative_one) != -1 || sign_9_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_10_to_s16(itoo->negative_one) != -1 || sign_10_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_11_to_s16(itoo->negative_one) != -1 || sign_11_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_12_to_s16(itoo->negative_one) != -1 || sign_12_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_13_to_s16(itoo->negative_one) != -1 || sign_13_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_14_to_s16(itoo->negative_one) != -1 || sign_14_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_15_to_s16(itoo->negative_one) != -1 || sign_15_to_s16(itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(17, itoo->negative_one) != -1 || sign_x_to_s32(17, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(18, itoo->negative_one) != -1 || sign_x_to_s32(18, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(19, itoo->negative_one) != -1 || sign_x_to_s32(19, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(20, itoo->negative_one) != -1 || sign_x_to_s32(20, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(21, itoo->negative_one) != -1 || sign_x_to_s32(21, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(22, itoo->negative_one) != -1 || sign_x_to_s32(22, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(23, itoo->negative_one) != -1 || sign_x_to_s32(23, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(24, itoo->negative_one) != -1 || sign_x_to_s32(24, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(25, itoo->negative_one) != -1 || sign_x_to_s32(25, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(26, itoo->negative_one) != -1 || sign_x_to_s32(26, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(27, itoo->negative_one) != -1 || sign_x_to_s32(27, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(28, itoo->negative_one) != -1 || sign_x_to_s32(28, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(29, itoo->negative_one) != -1 || sign_x_to_s32(29, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(30, itoo->negative_one) != -1 || sign_x_to_s32(30, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sign_x_to_s32(31, itoo->negative_one) != -1 || sign_x_to_s32(31, itoo->mostneg) != itoo->mostnegresult)
|
||||
FATALME;
|
||||
itoo++;
|
||||
|
||||
if(sizeof(int8) != 1 || sizeof(uint8) != 1)
|
||||
FATALME;
|
||||
|
||||
|
||||
if(!DoAntiNSOBugTest())
|
||||
return(0);
|
||||
|
||||
if(!DoLEPackerTest())
|
||||
return(0);
|
||||
|
||||
assert(uilog2(0) == 0);
|
||||
assert(uilog2(1) == 0);
|
||||
assert(uilog2(3) == 1);
|
||||
assert(uilog2(4095) == 11);
|
||||
assert(uilog2(0xFFFFFFFF) == 31);
|
||||
|
||||
QuickEndianRBOTest();
|
||||
|
||||
#if 0
|
||||
// Not really a math test.
|
||||
const char *test_paths[] = { "/meow", "/meow/cow", "\\meow", "\\meow\\cow", "\\\\meow", "\\\\meow\\cow",
|
||||
"/meow.", "/me.ow/cow.", "\\meow.", "\\me.ow\\cow.", "\\\\meow.", "\\\\meow\\cow.",
|
||||
"/meow.txt", "/me.ow/cow.txt", "\\meow.txt", "\\me.ow\\cow.txt", "\\\\meow.txt", "\\\\meow\\cow.txt"
|
||||
|
||||
"/meow", "/meow\\cow", "\\meow", "\\meow/cow", "\\\\meow", "\\\\meow/cow",
|
||||
"/meow.", "\\me.ow/cow.", "\\meow.", "/me.ow\\cow.", "\\\\meow.", "\\\\meow/cow.",
|
||||
"/meow.txt", "/me.ow\\cow.txt", "\\meow.txt", "\\me.ow/cow.txt", "\\\\meow.txt", "\\\\meow/cow.txt",
|
||||
"/bark///dog", "\\bark\\\\\\dog" };
|
||||
|
||||
for(unsigned i = 0; i < sizeof(test_paths) / sizeof(const char *); i++)
|
||||
{
|
||||
std::string file_path = std::string(test_paths[i]);
|
||||
std::string dir_path;
|
||||
std::string file_base;
|
||||
std::string file_ext;
|
||||
|
||||
MDFN_GetFilePathComponents(file_path, &dir_path, &file_base, &file_ext);
|
||||
|
||||
printf("%s ------ dir=%s --- base=%s --- ext=%s\n", file_path.c_str(), dir_path.c_str(), file_base.c_str(), file_ext.c_str());
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return(1);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#ifndef __MDFN_TESTS_H
|
||||
#define __MDFN_TESTS_H
|
||||
|
||||
bool MDFN_RunMathTests(void);
|
||||
|
||||
#endif
|
||||
|
@ -1,167 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "tblur.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16 a, b, c, d;
|
||||
} HQPixelEntry;
|
||||
|
||||
static uint32 *BlurBuf = NULL;
|
||||
static uint32 AccumBlurAmount; // max of 16384, infinite blur!
|
||||
static HQPixelEntry *AccumBlurBuf = NULL;
|
||||
|
||||
void TBlur_Init(void)
|
||||
{
|
||||
std::string sn = MDFNGameInfo->shortname;
|
||||
|
||||
if(MDFN_GetSettingB(std::string(sn + "." + std::string("tblur")).c_str()))
|
||||
{
|
||||
AccumBlurAmount = (uint32)(16384 * MDFN_GetSettingF(std::string(sn + "." + std::string("tblur.accum.amount")).c_str()) / 100);
|
||||
if(MDFN_GetSettingB(std::string(sn + "." + std::string("tblur.accum")).c_str()))
|
||||
{
|
||||
AccumBlurBuf = (HQPixelEntry *)calloc(sizeof(HQPixelEntry), MDFNGameInfo->fb_width * MDFNGameInfo->fb_height);
|
||||
MDFN_printf(_("Video temporal frame blur enabled with accumulation: %f.\n"), (double)AccumBlurAmount * 100 / 16384);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlurBuf = (uint32 *)calloc(4, MDFNGameInfo->fb_width * MDFNGameInfo->fb_height);
|
||||
MDFN_printf(_("Video temporal frame blur enabled.\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TBlur_Run(EmulateSpecStruct *espec)
|
||||
{
|
||||
MDFN_Surface *surface = espec->surface;
|
||||
uint32 *pXBuf = surface->pixels;
|
||||
const uint32 bb_pitch = MDFNGameInfo->fb_width;
|
||||
|
||||
//printf("%d %d %d %d\n", espec->surface->format.Rshift, espec->surface->format.Gshift, espec->surface->format.Bshift, espec->surface->format.Ashift);
|
||||
if(AccumBlurBuf)
|
||||
{
|
||||
for(int y = 0; y < espec->DisplayRect.h; y++)
|
||||
{
|
||||
int xw = espec->DisplayRect.w;
|
||||
int xs = espec->DisplayRect.x;
|
||||
|
||||
if(espec->LineWidths[0].w != ~0)
|
||||
{
|
||||
xw = espec->LineWidths[espec->DisplayRect.y + y].w;
|
||||
xs = espec->LineWidths[espec->DisplayRect.y + y].x;
|
||||
}
|
||||
|
||||
if(AccumBlurAmount == 8192)
|
||||
{
|
||||
for(int x = 0; x < xw; x++)
|
||||
{
|
||||
uint32 color;
|
||||
HQPixelEntry mixcolor;
|
||||
|
||||
color = pXBuf[(y + espec->DisplayRect.y) * surface->pitch32 + xs + x];
|
||||
mixcolor = AccumBlurBuf[y * bb_pitch + x];
|
||||
mixcolor.a = ((uint32)mixcolor.a + ((color & 0xFF) << 8)) >> 1;
|
||||
mixcolor.b = ((uint32)mixcolor.b + ((color & 0xFF00))) >> 1;
|
||||
mixcolor.c = ((uint32)mixcolor.c + ((color & 0xFF0000) >> 8)) >> 1;
|
||||
mixcolor.d = ((uint32)mixcolor.d + ((color & 0xFF000000) >> 16)) >> 1;
|
||||
|
||||
AccumBlurBuf[y * bb_pitch + x] = mixcolor;
|
||||
|
||||
pXBuf[(y + espec->DisplayRect.y) * surface->pitch32 + xs + x] = ((mixcolor.a >> 8) << 0) | ((mixcolor.b >> 8) << 8) |
|
||||
((mixcolor.c >> 8) << 16) | ((mixcolor.d >> 8) << 24);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 InvAccumBlurAmount = 16384 - AccumBlurAmount;
|
||||
|
||||
for(int x = 0; x < xw; x++)
|
||||
{
|
||||
uint32 color;
|
||||
HQPixelEntry mixcolor;
|
||||
color = pXBuf[(y + espec->DisplayRect.y) * surface->pitch32 + xs + x];
|
||||
|
||||
mixcolor = AccumBlurBuf[y * bb_pitch + x];
|
||||
mixcolor.a = ((uint32)mixcolor.a * AccumBlurAmount + InvAccumBlurAmount * ((color & 0xFF) << 8)) >> 14;
|
||||
mixcolor.b = ((uint32)mixcolor.b * AccumBlurAmount + InvAccumBlurAmount * ((color & 0xFF00))) >> 14;
|
||||
mixcolor.c = ((uint32)mixcolor.c * AccumBlurAmount + InvAccumBlurAmount * ((color & 0xFF0000) >> 8)) >> 14;
|
||||
mixcolor.d = ((uint32)mixcolor.d * AccumBlurAmount + InvAccumBlurAmount * ((color & 0xFF000000) >> 16)) >> 14;
|
||||
AccumBlurBuf[y * bb_pitch + x] = mixcolor;
|
||||
|
||||
pXBuf[(y + espec->DisplayRect.y) * surface->pitch32 + xs + x] = ((mixcolor.a >> 8) << 0) | ((mixcolor.b >> 8) << 8) |
|
||||
((mixcolor.c >> 8) << 16) | ((mixcolor.d >> 8) << 24);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if(BlurBuf)
|
||||
{
|
||||
for(int y = 0; y < espec->DisplayRect.h; y++)
|
||||
{
|
||||
int xw = espec->DisplayRect.w;
|
||||
int xs = espec->DisplayRect.x;
|
||||
|
||||
if(espec->LineWidths[0].w != ~0)
|
||||
{
|
||||
xw = espec->LineWidths[espec->DisplayRect.y + y].w;
|
||||
xs = espec->LineWidths[espec->DisplayRect.y + y].x;
|
||||
}
|
||||
|
||||
for(int x = 0; x < xw; x++)
|
||||
{
|
||||
uint32 color, mixcolor;
|
||||
color = pXBuf[(y + espec->DisplayRect.y) * surface->pitch32 + xs + x];
|
||||
|
||||
mixcolor = BlurBuf[y * bb_pitch + x];
|
||||
BlurBuf[y * bb_pitch + x] = color;
|
||||
|
||||
// Needs 64-bit
|
||||
#ifdef HAVE_NATIVE64BIT
|
||||
color = ((((uint64)color + mixcolor) - ((color ^ mixcolor) & 0x01010101))) >> 1;
|
||||
#else
|
||||
color = ((((color & 0x00FF00FF) + (mixcolor & 0x00FF00FF)) >> 1) & 0x00FF00FF) | (((((color & 0xFF00FF00) >> 1) + ((mixcolor & 0xFF00FF00) >> 1))) & 0xFF00FF00);
|
||||
#endif
|
||||
|
||||
// color = (((color & 0xFF) + (mixcolor & 0xFF)) >> 1) | ((((color & 0xFF00) + (mixcolor & 0xFF00)) >> 1) & 0xFF00) |
|
||||
// ((((color & 0xFF0000) + (mixcolor & 0xFF0000)) >> 1) & 0xFF0000) | ((((color >> 24) + (mixcolor >> 24)) >> 1) << 24);
|
||||
pXBuf[(y + espec->DisplayRect.y) * surface->pitch32 + xs + x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TBlur_Kill(void)
|
||||
{
|
||||
if(BlurBuf)
|
||||
{
|
||||
free(BlurBuf);
|
||||
BlurBuf = NULL;
|
||||
}
|
||||
if(AccumBlurBuf)
|
||||
{
|
||||
free(AccumBlurBuf);
|
||||
AccumBlurBuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool TBlur_IsOn(void)
|
||||
{
|
||||
return(BlurBuf || AccumBlurBuf);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#ifndef __MDFN_TBLUR_H
|
||||
#define __MDFN_TBLUR_H
|
||||
|
||||
#include "../video.h"
|
||||
|
||||
void TBlur_Init(void);
|
||||
void TBlur_Kill(void);
|
||||
void TBlur_Run(EmulateSpecStruct *espec);
|
||||
bool TBlur_IsOn(void);
|
||||
|
||||
#endif
|
@ -2,6 +2,5 @@
|
||||
#include "../video.h"
|
||||
#include "../general.h"
|
||||
#include "../state.h"
|
||||
#include "../movie.h"
|
||||
#include "../driver.h"
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include "mednafen/git.h"
|
||||
#include "mednafen/general.h"
|
||||
#include "mednafen/mednafen-driver.h"
|
||||
#include "mednafen/netplay-driver.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include <iostream>
|
||||
@ -121,8 +120,6 @@ int MDFND_UnlockMutex(MDFN_Mutex *lock)
|
||||
|
||||
void MDFND_SendData(const void*, uint32) {}
|
||||
void MDFND_RecvData(void *, uint32) {}
|
||||
void MDFND_NetplayText(const uint8*, bool) {}
|
||||
void MDFND_NetworkClose() {}
|
||||
|
||||
uint32 MDFND_GetTime()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user