Files
2015-09-03 01:20:11 -07:00

1196 lines
34 KiB
C

/*
PokeMini - Pokémon-Mini Emulator
Copyright (C) 2009-2015 JustBurn
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "PokeMini.h"
#include "Endianess.h"
#include <time.h>
// Dependencies
#ifndef NO_ZIP
#include "unzip.h"
#endif
// Include Free BIOS
#include "freebios.h"
int PokeMini_FreeBIOS = 0; // Using freebios?
int PokeMini_Flags = 0; // Configuration flags
uint8_t PM_BIOS[4096]; // Pokemon-Mini BIOS ($000000 to $000FFF, 4096)
uint8_t PM_RAM[8192]; // Pokemon-Mini RAM ($001000 to $002100, 4096 + 256)
uint8_t *PM_ROM = NULL; // Pokemon-Mini ROM ($002100 to $1FFFFF, Up to 2MB)
int PM_ROM_Alloc = 0; // Pokemon-Mini ROM Allocated on memory?
int PM_ROM_Size = 0; // Pokemon-Mini ROM Size
int PM_ROM_Mask = 0; // Pokemon-Mini ROM Mask
int PokeMini_LCDMode = 0; // LCD Mode
int PokeMini_ColorFormat = 0; // Color Format (0 = 8x8, 1 = 4x4)
int PokeMini_HostBattStatus = 0;// Host battery status
int PokeMini_RumbleAnim = 0; // Rumble animation
int PokeMini_RumbleAmount[4] = { -2, 1, -1, 2 };
const int PokeMini_RumbleAmountTable[16] = {
0, 0, 0, 0,
-1, 0, 1, 0,
-1, 1, -1, 1,
-2, 1, -1, 2,
};
const uint8_t PM_IO_INIT[256] = {
0x7F, 0x20, 0x5C, 0xff, 0xff, 0xff, 0xff, 0xff, // $00~$07 System Control
0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, // $08~$0F Second Counter
0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $10~$17 Battery Sensor
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, // $18~$1F Timers Controls
0x00, 0x30, 0x02, 0x00, 0x02, 0x00, 0x00, 0x40, // $20~$27 IRQ
0x00, 0xC0, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, // $28~$2F IRQ
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, // $30~$37 Timer 1
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, // $38~$3F Timer 2
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // $40~$47 256Hz Counter + ???
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, // $48~$4F Timer 3
0xFF, 0x00, 0xFF, 0x00, 0x01, 0x01, 0xff, 0xff, // $50~$57 Keypad + ???
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $58~$5F Unused
0x32, 0x64, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, // $60~$67 I/O
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $68~$6F Unused
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $70~$77 Audio + ???
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $78~$7F Unused
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // $80~$87 PRC
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // $88~$8F PRC
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $90~$97 Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $98~$9F Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $A0~$A7 Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $A8~$AF Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $B0~$B7 Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $B8~$BF Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $C0~$C7 Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $C8~$CF Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $D0~$D7 Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $D8~$DF Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $E0~$E7 Unused
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // $E8~$EF Unused
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // $F0~$F7 ???
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0xFF // $F8~$FF LCD I/O
};
// Callbacks
void (*PokeMini_OnAllocMIN)(int newsize, int success) = NULL;
void (*PokeMini_OnUnzipError)(const char *zipfile, const char *reason) = NULL;
void (*PokeMini_OnLoadBIOSFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnLoadMINFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnLoadColorFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnLoadEEPROMFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnSaveEEPROMFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnLoadStateFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnSaveStateFile)(const char *filename, int success) = NULL;
void (*PokeMini_OnReset)(int hardreset) = NULL;
int (*PokeMini_CustomLoadEEPROM)(const char *filename) = NULL;
int (*PokeMini_CustomSaveEEPROM)(const char *filename) = NULL;
// Number of cycles to process on hardware
int PokeHWCycles = 0;
// Create emulator and all interfaces
int PokeMini_Create(int flags, int soundfifo)
{
// Clear BIOS & RAM
memset(PM_BIOS, 0xFF, 4096);
memset(PM_RAM, 0xFF, 8192); // RAM + IO
// Dummy ROM (In case LoadROM fail)
PM_ROM_Alloc = 0;
PM_ROM_Mask = 0x1FFF;
PM_ROM_Size = 0x2000;
PM_ROM = (unsigned char *)PM_RAM;
// Clear & Init State
memcpy(PM_IO, PM_IO_INIT, 256);
if (!MinxCPU_Create()) return 0;
if (!MinxTimers_Create()) return 0;
if (!MinxIO_Create()) return 0;
if (!MinxIRQ_Create()) return 0;
if (!MinxPRC_Create()) return 0;
if (!MinxColorPRC_Create()) return 0;
if (!MinxLCD_Create()) return 0;
if (!MinxAudio_Create(soundfifo, soundfifo)) return 0;
// Initialize variables
PokeMini_Flags = flags;
// Load FreeBIOS
PokeMini_LoadFreeBIOS();
#ifndef PERFORMANCE
// Initialize multicart
NewMulticart();
SetMulticart(CommandLine.multicart);
#endif
return 1;
}
// Destroy emulator and all interfaces
void PokeMini_Destroy()
{
// Destroy all interfaces
MinxAudio_Destroy();
MinxLCD_Destroy();
MinxPRC_Destroy();
MinxColorPRC_Destroy();
MinxIRQ_Destroy();
MinxIO_Destroy();
MinxTimers_Destroy();
MinxCPU_Destroy();
// Free ROM if was allocated
if (PM_ROM_Alloc) {
free(PM_ROM);
PM_ROM = NULL;
PM_ROM_Alloc = 0;
}
// Free color info
PokeMini_FreeColorInfo();
}
// Apply changes from command lines
void PokeMini_ApplyChanges()
{
int i;
if (CommandLine.low_battery != 2) MinxIO_BatteryLow(CommandLine.low_battery);
PokeMini_SetLCDMode(CommandLine.lcdmode);
PokeMini_SetVideo(PokeMini_VideoCurrent, PokeMini_VideoDepth, CommandLine.lcdfilter, CommandLine.lcdmode);
MinxAudio_ChangeFilter(CommandLine.piezofilter);
for (i=0; i<4; i++) PokeMini_RumbleAmount[i] = PokeMini_RumbleAmountTable[((CommandLine.rumblelvl & 3) << 2) | i];
}
// User press or release a Pokemon-Mini key
void PokeMini_KeypadEvent(uint8_t key, int pressed)
{
MinxIO_Keypad(key, pressed);
}
// Low power battery emulation
void PokeMini_LowPower(int enable)
{
if (CommandLine.low_battery == 2) {
PokeMini_HostBattStatus = enable;
if (PokeMini_BatteryStatus != enable) MinxIO_BatteryLow(enable);
}
}
// Set LCD mode
void PokeMini_SetLCDMode(int mode)
{
PokeMini_LCDMode = mode;
MinxPRC_Render = MinxPRC_Render_Mono;
if (mode == LCDMODE_COLORS) {
if (PokeMini_ColorFormat == 1) MinxPRC_Render = MinxPRC_Render_Color4;
else MinxPRC_Render = MinxPRC_Render_Color8;
}
}
// Generate rumble offset
int PokeMini_GenRumbleOffset(int pitch)
{
return PokeMini_RumbleAmount[PokeMini_RumbleAnim++ & 3] * pitch;
}
// Load BIOS from file
int PokeMini_LoadBIOSFile(const char *filename)
{
FILE *fbios;
int readbytes;
// Open file
fbios = fopen(filename, "rb");
if (fbios == NULL) {
if (PokeMini_OnLoadBIOSFile) PokeMini_OnLoadBIOSFile(filename, -1);
return 0;
}
// Read content
readbytes = fread(PM_BIOS, 1, 4096, fbios);
PokeMini_FreeBIOS = 0;
// Close file
fclose(fbios);
// Callback
if (PokeMini_OnLoadBIOSFile) PokeMini_OnLoadBIOSFile(filename, (readbytes == 4096) ? 1 : 0);
return (readbytes == 4096);
}
// Save BIOS from file
int PokeMini_SaveBIOSFile(const char *filename)
{
FILE *fbios;
int writebytes;
// Open file
fbios = fopen(filename, "wb");
if (fbios == NULL) {
return 0;
}
// Read content
writebytes = fwrite(PM_BIOS, 1, 4096, fbios);
// Close file
fclose(fbios);
return (writebytes == 4096);
}
// Load FreeBIOS
int PokeMini_LoadFreeBIOS()
{
PokeMini_FreeBIOS = 1;
memcpy(PM_BIOS, FreeBIOS, 4096);
return 1;
}
// New MIN ROM
int PokeMini_NewMIN(uint32_t size)
{
// Allocate ROM and set cartridge size
if (PM_ROM_Alloc) {
free(PM_ROM);
PM_ROM = NULL;
PM_ROM_Alloc = 0;
}
PM_ROM_Mask = GetMultiple2Mask(size);
PM_ROM_Size = PM_ROM_Mask + 1;
PM_ROM = (uint8_t *)malloc(PM_ROM_Size);
if (!PM_ROM) {
if (PokeMini_OnAllocMIN) PokeMini_OnAllocMIN(PM_ROM_Size, 0);
return 0;
}
memset(PM_ROM, 0xFF, PM_ROM_Size);
PM_ROM_Alloc = 1;
if (PokeMini_OnAllocMIN) PokeMini_OnAllocMIN(PM_ROM_Size, 1);
return 1;
}
// Load MIN ROM
int PokeMini_LoadMINFile(const char *filename)
{
FILE *fi;
int size, readbytes;
// Open file
fi = fopen(filename, "rb");
if (fi == NULL) {
if (PokeMini_OnLoadMINFile) PokeMini_OnLoadMINFile(filename, -1);
return 0;
}
// Check filesize
fseek(fi, 0, SEEK_END);
size = ftell(fi);
// Check if size is valid
if ((size <= 0x2100) || (size > 0x200000)) {
fclose(fi);
if (PokeMini_OnLoadMINFile) PokeMini_OnLoadMINFile(filename, -2);
return 0;
}
// Free existing color information
PokeMini_FreeColorInfo();
// Allocate ROM and set cartridge size
if (!PokeMini_NewMIN(size)) {
fclose(fi);
return 0;
}
// Read content
fseek(fi, 0, SEEK_SET);
readbytes = fread(PM_ROM, 1, size, fi);
fclose(fi);
// Callback
if (PokeMini_OnLoadMINFile) PokeMini_OnLoadMINFile(filename, (readbytes == size) ? 1 : 0);
NewMulticart();
return (readbytes == size);
}
// Save MIN ROM
int PokeMini_SaveMINFile(const char *filename)
{
FILE *fi;
int writebytes;
// Open file
fi = fopen(filename, "wb");
if (fi == NULL) {
return 0;
}
// Write content
writebytes = fwrite(PM_ROM, 1, PM_ROM_Size, fi);
fclose(fi);
return (writebytes == PM_ROM_Size);
}
// Set MIN from memory
int PokeMini_SetMINMem(uint8_t *mem, int size)
{
if (PM_ROM_Alloc) { free(PM_ROM); PM_ROM = NULL; PM_ROM_Alloc = 0; }
PM_ROM_Mask = GetMultiple2Mask(size);
PM_ROM_Size = PM_ROM_Mask + 1;
PM_ROM = mem;
PM_ROM_Alloc = 0;
NewMulticart();
return 1;
}
// Free color information
void PokeMini_FreeColorInfo()
{
if (PRCColorMap) {
free(PRCColorMap);
PRCColorMap = NULL;
}
}
// Syncronize host time
int PokeMini_SyncHostTime()
{
#ifndef NO_RTC
// Modify EEPROM for host time
if (CommandLine.updatertc == 2) {
time_t tim = time(NULL);
struct tm *now = localtime(&tim);
MinxIO_SetTimeStamp(now->tm_year % 100, now->tm_mon+1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
PMR_SEC_CTRL = 0x01;
MinxTimers.SecTimerCnt = 0;
return 1;
}
#endif
return 0;
}
#define DATAREADFROMFILE(var, size) {\
if (stream(var, size, stream_ptr) != size) {\
return 0;\
}\
}
const uint8_t RemapMINC10_11[16] = {
0, 1, 2, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};
// Load color information from stream, note that PokeMini_OnLoadColorFile callback isn't called!
int PokeMini_LoadColorStream(TPokeMini_StreamIO stream, void *stream_ptr)
{
uint8_t hdr[4], vcod[4], reserved[16];
uint32_t maptiles, mapoffset, bytespertile;
int i, readbytes;
// Check header
DATAREADFROMFILE(hdr, 4); // ID
if ((hdr[0] != 'M') || (hdr[1] != 'I') || (hdr[2] != 'N') || (hdr[3] != 'c')) return 0;
DATAREADFROMFILE(vcod, 4); // VCode
if (vcod[0] != 0x01) return 0; // Only version 1 supported
if (vcod[1] > 0x01) return 0; // Only color type 0 and 1 are valid
PRCColorFlags = vcod[2];
PokeMini_ColorFormat = vcod[1];
DATAREADFROMFILE(&maptiles, 4); // Number of map tiles
maptiles = Endian32(maptiles);
DATAREADFROMFILE(&mapoffset, 4); // Map offset in tiles
mapoffset = Endian32(mapoffset);
DATAREADFROMFILE(reserved, 16); // Reserved area
// PM ROM Max is 2MB, that's 256K Map Tiles
if (maptiles > 262144) return 0;
if (mapoffset > 262144) return 0;
if (PokeMini_ColorFormat == 1) bytespertile = 8;
else bytespertile = 2;
// Free existing color information
PokeMini_FreeColorInfo();
PRCColorOffset = 0;
PRCColorTop = (uint8_t *)0;
// Create and load map
PRCColorMap = (uint8_t *)malloc(maptiles * bytespertile);
for (i=0; i<maptiles * bytespertile; i++) PRCColorMap[i] = 0x00;
readbytes = stream(PRCColorMap, maptiles * bytespertile, stream_ptr);
// Setup offset and top
PRCColorOffset = mapoffset * bytespertile;
PRCColorTop = (uint8_t *)PRCColorMap + (maptiles * bytespertile);
// Version 0.4.5 have old color palette, remap colors to the new one
if (!(PRCColorFlags & 1)) {
for (i=0; i<maptiles * bytespertile; i++) {
PRCColorMap[i] = RemapMINC10_11[PRCColorMap[i] & 15] | (PRCColorMap[i] & 0xF0);
}
}
return (readbytes > 0);
}
// Internal: Stream from file
int PokeMini_StreamFromFile(void *data, int size, void *ptr)
{
return fread(data, 1, size, (FILE *)ptr);
}
// Load color information from file, MIN must be loaded first
int PokeMini_LoadColorFile(const char *filename)
{
FILE *fi;
int res;
// Open file
fi = fopen(filename, "rb");
if (fi == NULL) return 0; // Silently exit
// Read color information
res = PokeMini_LoadColorStream(PokeMini_StreamFromFile, (void *)fi);
// Done
fclose(fi);
// Callback
if (PokeMini_OnLoadColorFile) PokeMini_OnLoadColorFile(filename, res);
return res;
}
// Load EEPROM
int PokeMini_LoadEEPROMFile(const char *filename)
{
FILE *fi;
int readbytes, success;
// Custom EEPROM load
if (PokeMini_CustomLoadEEPROM) {
success = PokeMini_CustomLoadEEPROM(filename);
PokeMini_OnLoadEEPROMFile(filename, success);
return success;
}
// Open file
fi = fopen(filename, "rb");
if (fi == NULL) {
if (PokeMini_OnLoadEEPROMFile) PokeMini_OnLoadEEPROMFile(filename, -1);
return 0;
}
// Read content
readbytes = fread(EEPROM, 1, 8192, fi);
fclose(fi);
// Callback
if (PokeMini_OnLoadEEPROMFile) PokeMini_OnLoadEEPROMFile(filename, (readbytes == 8192) ? 1 : 0);
return (readbytes == 8192);
}
// Save EEPROM
int PokeMini_SaveEEPROMFile(const char *filename)
{
FILE *fo;
int writebytes, success;
// Custom EEPROM save
if (PokeMini_CustomSaveEEPROM) {
success = PokeMini_CustomSaveEEPROM(filename);
PokeMini_OnSaveEEPROMFile(filename, success);
return success;
}
// Open file
fo = fopen(filename, "wb");
if (fo == NULL) {
if (PokeMini_OnSaveEEPROMFile) PokeMini_OnSaveEEPROMFile(filename, -1);
return 0;
}
// Read content
writebytes = fwrite(EEPROM, 1, 8192, fo);
fclose(fo);
// Callback
if (PokeMini_OnSaveEEPROMFile) PokeMini_OnSaveEEPROMFile(filename, (writebytes == 8192) ? 1 : 0);
return (writebytes == 8192);
}
// Check emulator state
int PokeMini_CheckSSFile(const char *statefile, char *romfile)
{
FILE *fi;
int readbytes;
char PMiniStr[128];
uint32_t PMiniID;
// Open file
fi = fopen(statefile, "rb");
if (fi == NULL) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -1);
return 0;
}
// Read content
PMiniStr[12] = 0;
readbytes = fread(PMiniStr, 1, 12, fi); // Read File ID
if ((readbytes != 12) || strcmp(PMiniStr, "PokeMiniStat")) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -2);
return 0;
}
readbytes = fread(&PMiniID, 1, 4, fi); // Read State ID
if ((readbytes != 4) || (PMiniID != PokeMini_ID)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -3);
return 0;
}
readbytes = fread(PMiniStr, 1, 128, fi); // Read ROM related to state
if (readbytes != 128) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -4);
return 0;
}
PMiniStr[127] = 0;
if (romfile) strcpy(romfile, PMiniStr);
fclose(fi);
return 1;
}
// Load emulator state
int PokeMini_LoadSSFile(const char *statefile)
{
FILE *fi;
int readbytes;
char PMiniStr[128];
uint32_t PMiniID, StatTime, BSize;
// Open file
fi = fopen(statefile, "rb");
if (fi == NULL) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -1);
return 0;
}
// Read content
PMiniStr[12] = 0;
readbytes = fread(PMiniStr, 1, 12, fi); // Read File ID
if ((readbytes != 12) || strcmp(PMiniStr, "PokeMiniStat")) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -2);
return 0;
}
readbytes = fread(&PMiniID, 1, 4, fi); // Read State ID
PMiniID = Endian32(PMiniID);
if ((readbytes != 4) || (PMiniID != PokeMini_ID)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -3);
return 0;
}
readbytes = fread(PMiniStr, 1, 128, fi); // Read ROM related to state (discarded)
if (readbytes != 128) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -4);
return 0;
}
readbytes = fread(&StatTime, 1, 4, fi); // Read Time
StatTime = Endian32(StatTime);
if (readbytes != 4) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -4);
return 0;
}
// Read State Structure
PMiniStr[4] = 0;
while (!feof(fi)) {
readbytes = fread(PMiniStr, 1, 4, fi);
if (readbytes != 4) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
readbytes = fread(&BSize, 1, 4, fi);
BSize = Endian32(BSize);
if (readbytes != 4) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
if (!strcmp(PMiniStr, "RAM-")) { // RAM
readbytes = fread(PM_RAM, 1, 0x1000, fi);
if ((BSize != 0x1000) || (readbytes != 0x1000)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "REG-")) { // Register I/O
readbytes = fread(PM_IO, 1, 256, fi);
if ((BSize != 256) || (readbytes != 256)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "CPU-")) { // CPU
if (!MinxCPU_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "IRQ-")) { // IRQ
if (!MinxIRQ_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "TMR-")) { // Timers
if (!MinxTimers_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "PIO-")) { // Parallel IO
if (!MinxIO_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "PRC-")) { // PRC
if (!MinxPRC_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "CPM-")) { // Color PRC
if (!MinxColorPRC_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "LCD-")) { // LCD
if (!MinxLCD_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "LCD-")) { // Audio
if (!MinxAudio_LoadState(fi, BSize)) {
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, -5);
return 0;
}
} else if (!strcmp(PMiniStr, "END-")) {
break;
}
}
fclose(fi);
// Update RTC if requested
if (CommandLine.updatertc == 1) {
MinxTimers.SecTimerCnt += (uint32_t)time(NULL) - StatTime;
}
// Syncronize with host time
PokeMini_SyncHostTime();
// Callback
if (PokeMini_OnLoadStateFile) PokeMini_OnLoadStateFile(statefile, 1);
return 1;
}
// Save emulator state
int PokeMini_SaveSSFile(const char *statefile, const char *romfile)
{
FILE *fo;
char PMiniStr[128];
uint32_t PMiniID, StatTime, BSize;
// Open file
fo = fopen(statefile, "wb");
if (fo == NULL) {
if (PokeMini_OnSaveStateFile) PokeMini_OnSaveStateFile(statefile, -1);
return 0;
}
// Write content
fwrite((void *)"PokeMiniStat", 1, 12, fo); // Write File ID
PMiniID = PokeMini_ID;
fwrite(&PMiniID, 1, 4, fo); // Write State ID
memset(PMiniStr, 0, 128);
strcpy(PMiniStr, romfile);
fwrite(PMiniStr, 1, 128, fo); // Write ROM related to state
StatTime = Endian32((uint32_t)time(NULL));
fwrite(&StatTime, 1, 4, fo); // Write Time
// Read State Structure
// - RAM
fwrite((void *)"RAM-", 1, 4, fo);
BSize = Endian32(0x1000);
fwrite(&BSize, 1, 4, fo);
fwrite(PM_RAM, 1, 0x1000, fo);
// - Registers I/O
fwrite((void *)"REG-", 1, 4, fo);
BSize = Endian32(256);
fwrite(&BSize, 1, 4, fo);
fwrite(PM_IO, 1, 256, fo);
// - CPU Interface
fwrite((void *)"CPU-", 1, 4, fo);
MinxCPU_SaveState(fo);
// - IRQ Interface
fwrite((void *)"IRQ-", 1, 4, fo);
MinxIRQ_SaveState(fo);
// - Timers Interface
fwrite((void *)"TMR-", 1, 4, fo);
MinxTimers_SaveState(fo);
// - Parallel IO Interface
fwrite((void *)"PIO-", 1, 4, fo);
MinxIO_SaveState(fo);
// - PRC Interface
fwrite((void *)"PRC-", 1, 4, fo);
MinxPRC_SaveState(fo);
// - Color PRC Interface
fwrite((void *)"CPM-", 1, 4, fo);
MinxColorPRC_SaveState(fo);
// - LCD Interface
fwrite((void *)"LCD-", 1, 4, fo);
MinxLCD_SaveState(fo);
// - Audio Interface
fwrite((void *)"AUD-", 1, 4, fo);
MinxAudio_SaveState(fo);
// - EOF
fwrite((void *)"END-", 1, 4, fo);
BSize = Endian32(0);
fwrite(&BSize, 1, 4, fo);
fclose(fo);
// Callback
if (PokeMini_OnSaveStateFile) PokeMini_OnSaveStateFile(statefile, 1);
return 1;
}
#ifndef NO_ZIP
// Internal: Stream from ZIP
static int PokeMini_StreamFromZIP(void *data, int size, void *ptr)
{
return unzReadCurrentFile((unzFile)ptr, data, size);
}
// Load compressed MIN ROM
static int PokeMini_iLoadROMZip(const char *zipfile, int *colorloaded)
{
unzFile uf = NULL;
unz_global_info64 gi;
unz_file_info64 file_inf;
char filein[PMTMPV];
void *new_data;
int i, size, loaded = 0, cloaded = 0;
if (colorloaded) *colorloaded = 0;
// Open ZIP
uf = unzOpen(zipfile);
if (!uf) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Opening ZIP error");
return 0;
}
if (unzGetGlobalInfo64(uf, &gi) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Getting global info");
unzClose(uf);
return 0;
}
// Find and load MIN
for (i=0; i<gi.number_entry; i++) {
if (unzGetCurrentFileInfo64(uf, &file_inf, filein, PMTMPV, NULL, 0, NULL, 0) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Current file info");
unzClose(uf);
return 0;
}
if (ExtensionCheck(filein, ".min") && (!loaded)) {
if (unzLocateFile(uf, filein, 0) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "LocateFile failed");
unzClose(uf);
return 0;
}
if (unzOpenCurrentFile(uf) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Opening file error");
unzClose(uf);
return 0;
}
size = GetMultiple2(file_inf.uncompressed_size);
new_data = (void *)malloc(size);
memset(new_data, 0xFF, size);
if (!new_data) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Not enough memory");
unzCloseCurrentFile(uf);
unzClose(uf);
return 0;
}
size = unzReadCurrentFile(uf, new_data, file_inf.uncompressed_size);
if (size < 0) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Reading file error");
unzCloseCurrentFile(uf);
unzClose(uf);
return 0;
}
PokeMini_FreeColorInfo(); // Free existing color information
PokeMini_SetMINMem((uint8_t *)new_data, file_inf.uncompressed_size);
PM_ROM_Alloc = 1;
if (unzCloseCurrentFile(uf) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Closing file error");
unzClose(uf);
return 0;
}
if (PokeMini_OnAllocMIN) PokeMini_OnAllocMIN(PM_ROM_Size, 1);
if (PokeMini_OnLoadMINFile) PokeMini_OnLoadMINFile(zipfile, 1);
loaded = 1;
break;
}
if ((i+1) < gi.number_entry) {
if (unzGoToNextFile(uf) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "No next file");
unzClose(uf);
return 0;
}
}
}
// Check if there's color information file
strcat(filein, "c");
if (!loaded) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "No ROM in ZIP");
unzClose(uf);
return 0;
} else {
if (unzGoToFirstFile(uf) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "No first file");
unzClose(uf);
return 0;
}
if (unzLocateFile(uf, filein, 0) == UNZ_OK) {
if (unzOpenCurrentFile(uf) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Opening file error");
unzClose(uf);
return 0;
}
if (!PokeMini_LoadColorStream(PokeMini_StreamFromZIP, uf)) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Reading file error");
unzCloseCurrentFile(uf);
unzClose(uf);
return 0;
}
if (unzCloseCurrentFile(uf) != UNZ_OK) {
if (PokeMini_OnUnzipError) PokeMini_OnUnzipError(zipfile, "Closing file error");
unzClose(uf);
return 0;
}
cloaded = 1;
if (colorloaded) *colorloaded = 1;
if (PokeMini_OnLoadColorFile) PokeMini_OnLoadColorFile(zipfile, 1);
}
}
// Close ZIP
unzClose(uf);
// Try to load color information from a file outside if wasn't found in the zip
if (!cloaded) {
// Filename based of the ROM inside the zip
RemoveExtension(filein);
strcat(filein, ".minc");
if (FileExist(filein)) {
if (PokeMini_LoadColorFile(filein)) {
if (colorloaded) *colorloaded = 1;
if (PokeMini_OnLoadColorFile) PokeMini_OnLoadColorFile(zipfile, 1);
return 1;
}
}
// Filename based of the zip
strcpy(filein, zipfile);
RemoveExtension(filein);
strcat(filein, ".minc");
if (FileExist(filein)) {
if (PokeMini_LoadColorFile(filein)) {
if (colorloaded) *colorloaded = 1;
if (PokeMini_OnLoadColorFile) PokeMini_OnLoadColorFile(zipfile, 1);
return 1;
}
}
}
return 1;
}
#endif
// Load MIN ROM (and others)
int PokeMini_LoadROM(const char *filename)
{
int colorloaded;
char tmp[PMTMPV];
// Save Individual EEPROM
if (!CommandLine.eeprom_share) {
if (PokeMini_EEPROMWritten && StringIsSet(CommandLine.eeprom_file)) {
PokeMini_EEPROMWritten = 0;
PokeMini_SaveEEPROMFile(CommandLine.eeprom_file);
}
}
#ifndef NO_ZIP
if (ExtensionCheck(filename, ".zip")) {
// Load new MIN ROM and Color Information inside zip
if (!PokeMini_iLoadROMZip(filename, &colorloaded)) return 0;
strcpy(CommandLine.min_file, filename);
} else
#endif
{
// Setup LCD mode based of color support
if (ExtensionCheck(filename, ".minc")) {
// Remove c and load new MIN ROM
strcpy(tmp, filename);
tmp[strlen(filename)-1] = 0;
if (!PokeMini_LoadMINFile(tmp)) return 0;
strcpy(CommandLine.min_file, tmp);
} else {
// Load new MIN ROM
if (!PokeMini_LoadMINFile(filename)) return 0;
strcpy(CommandLine.min_file, filename);
}
// Load Color Information
sprintf(tmp, "%sc", CommandLine.min_file);
if (FileExist(tmp) && PokeMini_LoadColorFile(tmp)) {
colorloaded = 1;
} else colorloaded = 0;
}
if (!colorloaded) {
if (CommandLine.lcdmode == 3) CommandLine.lcdmode = 0;
} else CommandLine.lcdmode = 3;
// Load Individual EEPROM
if (!CommandLine.eeprom_share) {
sprintf(CommandLine.eeprom_file, "%s.eep", CommandLine.min_file);
MinxIO_FormatEEPROM();
if (FileExist(CommandLine.eeprom_file)) PokeMini_LoadEEPROMFile(CommandLine.eeprom_file);
}
// Soft reset hardware
PokeMini_Reset(0);
// Apply changes
PokeMini_ApplyChanges();
return 1;
}
// Load all files from command-line, return false if require menu
int PokeMini_LoadFromCommandLines(const char *nobios, const char *noeeprom)
{
char tmp[PMTMPV], nomenu = 1;
// Load BIOS file
PokeMini_LoadFreeBIOS();
if (StringIsSet(CommandLine.bios_file)) {
if (FileExist(CommandLine.bios_file)) PokeMini_LoadBIOSFile(CommandLine.bios_file);
else {
PokeMini_GetCustomDir(tmp, PMTMPV);
PokeMini_GotoExecDir();
PokeMini_LoadBIOSFile(CommandLine.bios_file);
PokeMini_GotoCustomDir(tmp);
}
} else {
if (nobios) PokeDPrint(POKEMSG_OUT, "%s\n", nobios);
}
// Load ROM from Command-Line (or not)
if (StringIsSet(CommandLine.min_file)) {
nomenu = 0;
if (PokeMini_LoadROM(CommandLine.min_file)) nomenu = 1;
} else {
nomenu = 0;
if (StringIsSet(CommandLine.state_file)) {
if (PokeMini_CheckSSFile(CommandLine.state_file, tmp)) {
if (PokeMini_LoadROM(tmp)) nomenu = 1;
}
}
}
// Load EEPROM
if (CommandLine.eeprom_share) {
// Shared EEPROM
PokeMini_GetCustomDir(tmp, PMTMPV);
PokeMini_GotoExecDir();
MinxIO_FormatEEPROM();
if (StringIsSet(CommandLine.eeprom_file)) {
if (FileExist(CommandLine.eeprom_file)) PokeMini_LoadEEPROMFile(CommandLine.eeprom_file);
} else {
if (noeeprom) PokeDPrint(POKEMSG_OUT, "%s\n", noeeprom);
}
PokeMini_GotoCustomDir(tmp);
} else {
// Individual EEPROM
sprintf(CommandLine.eeprom_file, "%s.eep", CommandLine.min_file);
MinxIO_FormatEEPROM();
if (FileExist(CommandLine.eeprom_file)) PokeMini_LoadEEPROMFile(CommandLine.eeprom_file);
}
// Reset CPU (soft reset)
PokeMini_Reset(0);
// Load State
if (StringIsSet(CommandLine.state_file)) {
PokeMini_LoadSSFile(CommandLine.state_file);
}
return nomenu;
}
// Save all files from command-line
void PokeMini_SaveFromCommandLines(int exitemulator)
{
char tmp[PMTMPV];
// Save EEPROM
PokeMini_GetCustomDir(tmp, PMTMPV);
PokeMini_GotoExecDir();
if (PokeMini_EEPROMWritten && StringIsSet(CommandLine.eeprom_file)) {
PokeMini_EEPROMWritten = 0;
PokeMini_SaveEEPROMFile(CommandLine.eeprom_file);
}
PokeMini_GotoCustomDir(tmp);
// Save State
if (StringIsSet(CommandLine.state_file)) {
PokeMini_SaveSSFile(CommandLine.state_file, CommandLine.min_file);
}
}
// Use default callbacks messages
void PokeMini_OnUnzipError_Def(const char *zipfile, const char *reason)
{
PokeDPrint(POKEMSG_ERR, "Error decompressing %s: %s\n", zipfile, reason);
}
void PokeMini_OnLoadBIOSFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "BIOS '%s' loaded\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error loading BIOS '%s': file not found\nUsing FreeBIOS\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error loading BIOS '%s': read error\nUsing FreeBIOS\n", filename);
}
void PokeMini_OnLoadMINFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "ROM '%s' loaded\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error loading ROM '%s': file not found\n", filename);
else if (success == -2) PokeDPrint(POKEMSG_ERR, "Error loading ROM '%s': invalid size\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error loading ROM '%s', read error\n", filename);
}
void PokeMini_OnLoadColorFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "Color info '%s' loaded\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error loading color info '%s': file not found\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error loading color info '%s': read error\n", filename);
}
void PokeMini_OnLoadEEPROMFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "EEPROM '%s' loaded\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error loading EEPROM '%s': file not found\n", filename);
else if (success == -2) PokeDPrint(POKEMSG_ERR, "Error saving EEPROM '%s': device not found\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error loading EEPROM '%s': read error\n", filename);
}
void PokeMini_OnSaveEEPROMFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "EEPROM '%s' saved\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error saving EEPROM '%s': filename invalid\n", filename);
else if (success == -2) PokeDPrint(POKEMSG_ERR, "Error saving EEPROM '%s': device not found\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error saving EEPROM '%s': write error\n", filename);
}
void PokeMini_OnLoadStateFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "State '%s' loaded\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error loading state '%s': file not found\n", filename);
else if (success == -2) PokeDPrint(POKEMSG_ERR, "Error loading state '%s': invalid file\n", filename);
else if (success == -3) PokeDPrint(POKEMSG_ERR, "Error loading state '%s': wrong version\n", filename);
else if (success == -4) PokeDPrint(POKEMSG_ERR, "Error loading state '%s': invalid header\n", filename);
else if (success == -5) PokeDPrint(POKEMSG_ERR, "Error loading state '%s': invalid internal block\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error loading state '%s': read error\n", filename);
}
void PokeMini_OnSaveStateFile_Def(const char *filename, int success)
{
if (success == 1) PokeDPrint(POKEMSG_OUT, "State '%s' saved\n", filename);
else if (success == -1) PokeDPrint(POKEMSG_ERR, "Error saving state '%s': filename invalid\n", filename);
else PokeDPrint(POKEMSG_ERR, "Error saving state '%s': write error\n", filename);
}
void PokeMini_OnReset_Def(int hardreset)
{
}
void PokeMini_UseDefaultCallbacks()
{
PokeMini_OnAllocMIN = NULL;
PokeMini_OnUnzipError = PokeMini_OnUnzipError_Def;
PokeMini_OnLoadBIOSFile = PokeMini_OnLoadBIOSFile_Def;
PokeMini_OnLoadMINFile = PokeMini_OnLoadMINFile_Def;
PokeMini_OnLoadColorFile = PokeMini_OnLoadColorFile_Def;
PokeMini_OnLoadEEPROMFile = PokeMini_OnLoadEEPROMFile_Def;
PokeMini_OnSaveEEPROMFile = PokeMini_OnSaveEEPROMFile_Def;
PokeMini_OnLoadStateFile = PokeMini_OnLoadStateFile_Def;
PokeMini_OnSaveStateFile = PokeMini_OnSaveStateFile_Def;
PokeMini_OnReset = PokeMini_OnReset_Def;
}
// Reset emulation
void PokeMini_Reset(int hardreset)
{
char tmp[PMTMPV];
// Reset IO
if (hardreset) {
memset(PM_RAM, 0xFF, 8192);
memcpy(PM_IO, PM_IO_INIT, 256);
}
// Reset all components
MinxTimers_Reset(hardreset);
MinxIRQ_Reset(hardreset);
MinxIO_Reset(hardreset);
MinxPRC_Reset(hardreset);
MinxColorPRC_Reset(hardreset);
MinxLCD_Reset(hardreset);
MinxAudio_Reset(hardreset);
MinxCPU_Reset(hardreset);
// Change BIOS
if (!PokeMini_FreeBIOS && CommandLine.forcefreebios) {
PokeMini_LoadFreeBIOS();
}
if (PokeMini_FreeBIOS && !CommandLine.forcefreebios) {
PokeMini_LoadFreeBIOS();
if (StringIsSet(CommandLine.bios_file)) {
if (FileExist(CommandLine.bios_file)) PokeMini_LoadBIOSFile(CommandLine.bios_file);
else {
PokeMini_GetCustomDir(tmp, PMTMPV);
PokeMini_GotoExecDir();
if (FileExist(CommandLine.bios_file)) PokeMini_LoadBIOSFile(CommandLine.bios_file);
PokeMini_GotoCustomDir(tmp);
}
}
}
// Syncronize with host time
PokeMini_SyncHostTime();
#ifndef PERFORMANCE
// Set multicart type
SetMulticart(CommandLine.multicart);
#endif
// Callback
if (PokeMini_OnReset) PokeMini_OnReset(hardreset);
}