Cleanups for RetroFen

This commit is contained in:
twinaphex 2012-10-20 04:13:19 +02:00
parent 32a02748fe
commit e515b9ab96
33 changed files with 47 additions and 5947 deletions

View File

@ -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

View File

@ -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)
{
}

View File

@ -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

View File

@ -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(&sector_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
};

View File

@ -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

View File

@ -1,3 +0,0 @@
modified slightly for usage in Mednafen
ARM stuff not compiled in, for now.

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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"

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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());
}
//
//
}

View File

@ -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

View File

@ -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 &copyright, 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 &copyright, 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);
}
}

View File

@ -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 &copyright,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 &copyright, char **snames);
void Player_Draw(MDFN_Surface *surface, MDFN_Rect *dr, int CurrentSong, int16 *samples, int32 sampcount);
#endif

View File

@ -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 }

View File

@ -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());
}
}

View File

@ -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

View File

@ -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> &sections)
@ -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();
}

View File

@ -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);
}

View File

@ -1,7 +0,0 @@
#ifndef __MDFN_TESTS_H
#define __MDFN_TESTS_H
bool MDFN_RunMathTests(void);
#endif

View File

@ -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);
}

View File

@ -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

View File

@ -2,6 +2,5 @@
#include "../video.h"
#include "../general.h"
#include "../state.h"
#include "../movie.h"
#include "../driver.h"

View File

@ -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()
{