Merge branch 'master' into optimization/idle-loop-detection

Conflicts:
	CHANGES
This commit is contained in:
Jeffrey Pfau 2015-01-13 01:57:45 -08:00
commit 542662ca68
31 changed files with 474 additions and 208 deletions

View File

@ -16,6 +16,8 @@ Features:
- Rewinding of emulation
- Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter
- Support IPv6
- Save directory of last loaded file
- Support BPS patches
- Automatically detect and optimize out idle loops
Bugfixes:
- Qt: Fix issue with set frame sizes being the wrong height
@ -48,6 +50,7 @@ Bugfixes:
- Qt: Fix some cases where key mapping can break if focus is adjusted
- GBA Memory: Filter out top nybble of DMA addresses
- Debugger: Fix binary print putting spaces between digits
- GBA BIOS: Fix LZ77UnCompVram to use 16-bit loads from decompressed memory
Misc:
- Qt: Disable sync to video by default
- GBA: Exit cleanly on FATAL if the port supports it

View File

@ -312,7 +312,6 @@ static void _unLz77(struct GBA* gba, int width) {
int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set
source += 4;
int blocksRemaining = 0;
int block;
uint32_t disp;
int bytes;
int byte;
@ -321,24 +320,27 @@ static void _unLz77(struct GBA* gba, int width) {
if (blocksRemaining) {
if (blockheader & 0x80) {
// Compressed
block = cpu->memory.load8(cpu, source, 0) | (cpu->memory.load8(cpu, source + 1, 0) << 8);
int block = cpu->memory.load8(cpu, source + 1, 0) | (cpu->memory.load8(cpu, source, 0) << 8);
source += 2;
disp = dest - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
bytes = ((block & 0x00F0) >> 4) + 3;
disp = dest - (block & 0x0FFF) - 1;
bytes = (block >> 12) + 3;
while (bytes-- && remaining) {
--remaining;
byte = cpu->memory.load8(cpu, disp, 0);
++disp;
if (width == 2) {
byte = cpu->memory.load16(cpu, disp & ~1, 0);
if (dest & 1) {
byte >>= (disp & 1) * 8;
halfword |= byte << 8;
cpu->memory.store16(cpu, dest ^ 1, halfword, 0);
} else {
halfword = byte;
byte >>= (disp & 1) * 8;
halfword = byte & 0xFF;
}
} else {
byte = cpu->memory.load8(cpu, disp, 0);
cpu->memory.store8(cpu, dest, byte, 0);
}
++disp;
++dest;
}
} else {

View File

@ -112,7 +112,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
GBALoadState(gbaDebugger->context->gba, gbaDebugger->context->stateDir, dv->intValue);
GBALoadState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue);
}
static void _rewind(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@ -139,6 +139,6 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
GBASaveState(gbaDebugger->context->gba, gbaDebugger->context->stateDir, dv->intValue, true);
GBASaveState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, true);
}
#endif

View File

@ -522,7 +522,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
case REGION_CART_SRAM:
case REGION_CART_SRAM_MIRROR:
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
if (memory->savedata.type == SAVEDATA_NONE) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
GBALog(gba, GBA_LOG_INFO, "Detected SRAM savegame");
GBASavedataInitSRAM(&memory->savedata);
}
@ -678,7 +678,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
}
break;
case REGION_CART2_EX:
if (memory->savedata.type == SAVEDATA_NONE) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
GBALog(gba, GBA_LOG_INFO, "Detected EEPROM savegame");
GBASavedataInitEEPROM(&memory->savedata);
}
@ -734,7 +734,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
break;
case REGION_CART_SRAM:
case REGION_CART_SRAM_MIRROR:
if (memory->savedata.type == SAVEDATA_NONE) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
if (address == SAVEDATA_FLASH_BASE) {
GBALog(gba, GBA_LOG_INFO, "Detected Flash savegame");
GBASavedataInitFlash(&memory->savedata);
@ -1234,7 +1234,7 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
dest += destOffset;
--wordsRemaining;
} else if (destRegion == REGION_CART2_EX) {
if (memory->savedata.type == SAVEDATA_NONE) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
GBALog(gba, GBA_LOG_INFO, "Detected EEPROM savegame");
GBASavedataInitEEPROM(&memory->savedata);
}

199
src/gba/gba-overrides.c Normal file
View File

@ -0,0 +1,199 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gba-overrides.h"
#include "gba.h"
#include "gba-gpio.h"
#include "util/configuration.h"
static const struct GBACartridgeOverride _overrides[] = {
// Boktai: The Sun is in Your Hand
{ "U3IJ", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
{ "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
{ "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
// Boktai 2: Solar Boy Django
{ "U32J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
{ "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
{ "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
// Drill Dozer
{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
{ "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
// Final Fantasy Tactics Advance
{ "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 },
// Koro Koro Puzzle - Happy Panechu!
{ "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
// Mega Man Battle Network
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E },
// Pokemon Ruby
{ "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
// Pokemon Sapphire
{ "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
// Pokemon Emerald
{ "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
// Pokemon Mystery Dungeon
{ "B24J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "B24E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "B24P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "B24U", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// Pokemon FireRed
{ "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// Pokemon LeafGreen
{ "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// RockMan EXE 4.5 - Real Operation
{ "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 },
// Shin Bokura no Taiyou: Gyakushuu no Sabata
{ "U33J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
// Super Mario Advance 4
{ "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// Top Gun - Combat Zones
{ "A2YE", SAVEDATA_FORCE_NONE, GPIO_NONE, -1 },
// Wario Ware Twisted
{ "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
{ "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
{ "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
// Yoshi's Universal Gravitation
{ "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
{ "KYGE", SAVEDATA_EEPROM, GPIO_TILT, -1 },
{ "KYGP", SAVEDATA_EEPROM, GPIO_TILT, -1 },
{ { 0, 0, 0, 0 }, 0, 0, -1 }
};
bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOverride* override) {
override->savetype = SAVEDATA_AUTODETECT;
override->hardware = GPIO_NONE;
override->idleLoop = -1;
bool found;
if (override->id[0] == 'F') {
// Classic NES Series
override->savetype = SAVEDATA_EEPROM;
found = true;
} else {
int i;
for (i = 0; _overrides[i].id[0]; ++i) {
if (memcmp(override->id, _overrides[i].id, sizeof(override->id)) == 0) {
*override = _overrides[i];
found = true;
break;
}
}
}
if (config) {
char sectionName[16];
snprintf(sectionName, sizeof(sectionName), "override.%c%c%c%c", override->id[0], override->id[1], override->id[2], override->id[3]);
const char* savetype = ConfigurationGetValue(config, sectionName, "savetype");
const char* hardware = ConfigurationGetValue(config, sectionName, "hardware");
const char* idleLoop = ConfigurationGetValue(config, sectionName, "idleLoop");
if (savetype) {
if (strcasecmp(savetype, "SRAM") == 0) {
found = true;
override->savetype = SAVEDATA_SRAM;
} else if (strcasecmp(savetype, "EEPROM") == 0) {
found = true;
override->savetype = SAVEDATA_EEPROM;
} else if (strcasecmp(savetype, "FLASH512") == 0) {
found = true;
override->savetype = SAVEDATA_FLASH512;
} else if (strcasecmp(savetype, "FLASH1M") == 0) {
found = true;
override->savetype = SAVEDATA_FLASH1M;
} else if (strcasecmp(savetype, "NONE") == 0) {
found = true;
override->savetype = SAVEDATA_FORCE_NONE;
}
}
if (hardware) {
char* end;
long type = strtoul(hardware, &end, 0);
if (end && !*end) {
override->hardware = type;
found = true;
}
}
if (idleLoop) {
char* end;
uint32_t address = strtoul(idleLoop, &end, 16);
if (end && !*end) {
override->idleLoop = address;
found = true;
}
}
}
return found;
}
void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) {
GBASavedataForceType(&gba->memory.savedata, override->savetype);
if (override->hardware & GPIO_RTC) {
GBAGPIOInitRTC(&gba->memory.gpio);
}
if (override->hardware & GPIO_GYRO) {
GBAGPIOInitGyro(&gba->memory.gpio);
}
if (override->hardware & GPIO_RUMBLE) {
GBAGPIOInitRumble(&gba->memory.gpio);
}
if (override->hardware & GPIO_LIGHT_SENSOR) {
GBAGPIOInitLightSensor(&gba->memory.gpio);
}
if (override->hardware & GPIO_TILT) {
GBAGPIOInitTilt(&gba->memory.gpio);
}
gba->busyLoop = override->idleLoop;
}

27
src/gba/gba-overrides.h Normal file
View File

@ -0,0 +1,27 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GBA_OVERRIDES_H
#define GBA_OVERRIDES_H
#include "util/common.h"
#include "gba-savedata.h"
struct GBACartridgeOverride {
char id[4];
enum SavedataType savetype;
int hardware;
uint32_t idleLoop;
};
struct Configuration;
bool GBAOverrideFind(const struct Configuration*, struct GBACartridgeOverride* override);
bool GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* override);
struct GBA;
void GBAOverrideApply(struct GBA*, const struct GBACartridgeOverride*);
#endif

View File

@ -18,7 +18,7 @@ static void _flashErase(struct GBASavedata* savedata);
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
savedata->type = SAVEDATA_NONE;
savedata->type = SAVEDATA_AUTODETECT;
savedata->data = 0;
savedata->command = EEPROM_COMMAND_NULL;
savedata->flashState = FLASH_STATE_RAW;
@ -42,7 +42,8 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
case SAVEDATA_EEPROM:
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
break;
case SAVEDATA_NONE:
case SAVEDATA_FORCE_NONE:
case SAVEDATA_AUTODETECT:
break;
}
savedata->vf = 0;
@ -60,12 +61,13 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
case SAVEDATA_EEPROM:
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
break;
case SAVEDATA_NONE:
case SAVEDATA_FORCE_NONE:
case SAVEDATA_AUTODETECT:
break;
}
}
savedata->data = 0;
savedata->type = SAVEDATA_NONE;
savedata->type = SAVEDATA_AUTODETECT;
}
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) {
@ -94,7 +96,8 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
case SAVEDATA_EEPROM:
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
case SAVEDATA_NONE:
case SAVEDATA_AUTODETECT:
case SAVEDATA_FORCE_NONE:
return true;
}
} else if (savedata->vf) {
@ -122,14 +125,16 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type)
case SAVEDATA_SRAM:
GBASavedataInitSRAM(savedata);
break;
case SAVEDATA_NONE:
// TODO: Force none
case SAVEDATA_FORCE_NONE:
savedata->type = SAVEDATA_FORCE_NONE;
break;
case SAVEDATA_AUTODETECT:
break;
}
}
void GBASavedataInitFlash(struct GBASavedata* savedata) {
if (savedata->type == SAVEDATA_NONE) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_FLASH512;
}
if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
@ -142,7 +147,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
end = 0;
savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
} else {
end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
end = savedata->vf->size(savedata->vf);
if (end < SIZE_CART_FLASH512) {
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
flashSize = SIZE_CART_FLASH1M;
@ -157,7 +162,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
}
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
if (savedata->type == SAVEDATA_NONE) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_EEPROM;
} else {
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
@ -168,7 +173,7 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
end = 0;
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
} else {
end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
end = savedata->vf->size(savedata->vf);
if (end < SIZE_CART_EEPROM) {
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
}
@ -180,7 +185,7 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
}
void GBASavedataInitSRAM(struct GBASavedata* savedata) {
if (savedata->type == SAVEDATA_NONE) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_SRAM;
} else {
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
@ -191,7 +196,7 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) {
end = 0;
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
} else {
end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
end = savedata->vf->size(savedata->vf);
if (end < SIZE_CART_SRAM) {
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
}

View File

@ -11,7 +11,8 @@
struct VFile;
enum SavedataType {
SAVEDATA_NONE = 0,
SAVEDATA_AUTODETECT = -1,
SAVEDATA_FORCE_NONE = 0,
SAVEDATA_SRAM,
SAVEDATA_FLASH512,
SAVEDATA_FLASH1M,

View File

@ -175,22 +175,23 @@ static bool _loadPNGState(struct GBA* gba, struct VFile* vf) {
}
#endif
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) {
struct VFile* vf = GBAGetState(gba, dir, slot, true);
bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, bool screenshot) {
struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, true);
if (!vf) {
return false;
}
bool success = GBASaveStateNamed(gba, vf, screenshot);
bool success = GBASaveStateNamed(threadContext->gba, vf, screenshot);
vf->close(vf);
return success;
}
bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) {
struct VFile* vf = GBAGetState(gba, dir, slot, false);
bool GBALoadState(struct GBAThread* threadContext, struct VDir* dir, int slot) {
struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, false);
if (!vf) {
return false;
}
bool success = GBALoadStateNamed(gba, vf);
threadContext->rewindBufferSize = 0;
bool success = GBALoadStateNamed(threadContext->gba, vf);
vf->close(vf);
return success;
}

View File

@ -282,12 +282,13 @@ struct GBASerializedState {
};
struct VDir;
struct GBAThread;
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state);
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot);
bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot);
bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, bool screenshot);
bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot);
struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write);
bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot);
@ -296,7 +297,6 @@ bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf);
struct GBASerializedState* GBAAllocateState(void);
void GBADeallocateState(struct GBASerializedState* state);
struct GBAThread;
void GBARecordFrame(struct GBAThread* thread);
void GBARewindSettingsChanged(struct GBAThread* thread, int newCapacity, int newInterval);
void GBARewind(struct GBAThread* thread, int nStates);

View File

@ -7,6 +7,13 @@
#include "gba-io.h"
const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = {
{ 31457, 62914, 94371, 125829 },
{ 7864, 15728, 23592, 31457 },
{ 5242, 10485, 15728, 20971 },
{ 2621, 5242, 7864, 10485 }
};
static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) {
switch (mode) {
case SIO_NORMAL_8:

View File

@ -8,6 +8,10 @@
#include "util/common.h"
#define MAX_GBAS 4
extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS];
enum GBASIOMode {
SIO_NORMAL_8 = 0,
SIO_NORMAL_32 = 1,

View File

@ -8,6 +8,7 @@
#include "arm.h"
#include "gba.h"
#include "gba-config.h"
#include "gba-overrides.h"
#include "gba-serialize.h"
#include "debugger/debugger.h"
@ -141,6 +142,14 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
if (threadContext->rom) {
GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname);
struct GBACartridgeOverride override;
const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom;
memcpy(override.id, &cart->id, sizeof(override.id));
if (GBAOverrideFind(threadContext->overrides, &override)) {
GBAOverrideApply(&gba, &override);
}
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
GBALoadBIOS(&gba, threadContext->bios);
}

View File

@ -70,6 +70,7 @@ struct GBAThread {
const char* fname;
int activeKeys;
struct GBAAVStream* stream;
struct Configuration* overrides;
// Run-time options
int frameskip;

View File

@ -24,99 +24,6 @@ const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
static const size_t GBA_ROM_MAGIC_OFFSET = 2;
static const uint8_t GBA_ROM_MAGIC[] = { 0x00, 0xEA };
struct GBACartridgeOverride {
const char id[4];
enum SavedataType type;
int gpio;
uint32_t busyLoop;
};
static const struct GBACartridgeOverride _overrides[] = {
// Boktai: The Sun is in Your Hand
{ "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
{ "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
// Boktai 2: Solar Boy Django
{ "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
{ "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
// Drill Dozer
{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
{ "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
// Final Fantasy Tactics Advance
{ "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 },
// Koro Koro Puzzle - Happy Panechu!
{ "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
// Mega Man Battle Network
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E },
// Pokemon Ruby
{ "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
// Pokemon Sapphire
{ "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
// Pokemon Emerald
{ "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
{ "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
// Pokemon Mystery Dungeon
{ "B24J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "B24E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "B24P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "B24U", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// Pokemon FireRed
{ "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// Pokemon LeafGreen
{ "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// RockMan EXE 4.5 - Real Operation
{ "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 },
// Super Mario Advance 4
{ "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
{ "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
// Wario Ware Twisted
{ "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
{ "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
{ "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
// Yoshi's Universal Gravitation
{ "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
{ "KYGE", SAVEDATA_EEPROM, GPIO_TILT, -1 },
{ "KYGP", SAVEDATA_EEPROM, GPIO_TILT, -1 },
{ { 0, 0, 0, 0 }, 0, 0, -1 }
};
static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component);
static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh);
static void GBAProcessEvents(struct ARMCore* cpu);
@ -124,8 +31,6 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles);
static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
static void _checkOverrides(struct GBA* gba, uint32_t code);
void GBACreate(struct GBA* gba) {
gba->d.id = GBA_COMPONENT_MAGIC;
gba->d.init = GBAInit;
@ -440,7 +345,7 @@ void GBADetachDebugger(struct GBA* gba) {
void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname) {
gba->romVf = vf;
gba->pristineRomSize = vf->seek(vf, 0, SEEK_END);
gba->pristineRomSize = vf->size(vf);
vf->seek(vf, 0, SEEK_SET);
if (gba->pristineRomSize > SIZE_CART0) {
gba->pristineRomSize = SIZE_CART0;
@ -456,7 +361,6 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
GBASavedataInit(&gba->memory.savedata, sav);
GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
_checkOverrides(gba, ((struct GBACartridge*) gba->memory.rom)->id);
// TODO: error check
}
@ -491,8 +395,7 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
return;
}
gba->memory.rom = anonymousMemoryMap(patchedSize);
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize > patchedSize ? patchedSize : gba->memory.romSize);
if (!patch->applyPatch(patch, gba->memory.rom, patchedSize)) {
if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) {
mappedMemoryFree(gba->memory.rom, patchedSize);
gba->memory.rom = gba->pristineRom;
return;
@ -732,43 +635,3 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
}
}
void _checkOverrides(struct GBA* gba, uint32_t id) {
int i;
gba->busyLoop = -1;
if ((id & 0xFF) == 'F') {
GBALog(gba, GBA_LOG_DEBUG, "Found Classic NES Series game, using EEPROM saves");
GBASavedataInitEEPROM(&gba->memory.savedata);
return;
}
for (i = 0; _overrides[i].id[0]; ++i) {
const uint32_t* overrideId = (const uint32_t*) _overrides[i].id;
if (*overrideId == id) {
GBALog(gba, GBA_LOG_DEBUG, "Found override for game %s!", _overrides[i].id);
GBASavedataForceType(&gba->memory.savedata, _overrides[i].type);
if (_overrides[i].gpio & GPIO_RTC) {
GBAGPIOInitRTC(&gba->memory.gpio);
}
if (_overrides[i].gpio & GPIO_GYRO) {
GBAGPIOInitGyro(&gba->memory.gpio);
}
if (_overrides[i].gpio & GPIO_RUMBLE) {
GBAGPIOInitRumble(&gba->memory.gpio);
}
if (_overrides[i].gpio & GPIO_LIGHT_SENSOR) {
GBAGPIOInitLightSensor(&gba->memory.gpio);
}
if (_overrides[i].gpio & GPIO_TILT) {
GBAGPIOInitTilt(&gba->memory.gpio);
}
gba->busyLoop = _overrides[i].busyLoop;
return;
}
}
}

View File

@ -76,6 +76,7 @@ int main(int argc, char** argv) {
}
context.debugger = createDebugger(&args, &context);
context.overrides = &config.configTable;
char gameCode[5] = { 0 };
GBAConfigMap(&config, &opts);

View File

@ -75,6 +75,8 @@ public:
QList<QString> getMRU() const;
void setMRU(const QList<QString>& mru);
Configuration* overrides() { return &m_config.configTable; } // TODO: Make this not return the whole table
public slots:
void setOption(const char* key, bool value);
void setOption(const char* key, int value);

View File

@ -204,6 +204,7 @@ void GameController::openGame() {
if (!GBAThreadStart(&m_threadContext)) {
m_gameOpen = false;
emit gameFailed();
}
}
@ -360,7 +361,7 @@ void GameController::setSkipBIOS(bool set) {
void GameController::loadState(int slot) {
threadInterrupt();
GBALoadState(m_threadContext.gba, m_threadContext.stateDir, slot);
GBALoadState(&m_threadContext, m_threadContext.stateDir, slot);
threadContinue();
emit stateLoaded(&m_threadContext);
emit frameAvailable(m_drawContext);
@ -368,7 +369,7 @@ void GameController::loadState(int slot) {
void GameController::saveState(int slot) {
threadInterrupt();
GBASaveState(m_threadContext.gba, m_threadContext.stateDir, slot, true);
GBASaveState(&m_threadContext, m_threadContext.stateDir, slot, true);
threadContinue();
}

View File

@ -22,6 +22,7 @@ extern "C" {
struct GBAAudio;
struct GBAVideoSoftwareRenderer;
struct Configuration;
class QThread;
@ -53,6 +54,7 @@ public:
bool videoSync() const { return m_videoSync; }
void setInputController(InputController* controller) { m_inputController = controller; }
void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; }
#ifdef USE_GDB_STUB
ARMDebugger* debugger();
@ -66,6 +68,7 @@ signals:
void gamePaused(GBAThread*);
void gameUnpaused(GBAThread*);
void gameCrashed(const QString& errorMessage);
void gameFailed();
void stateLoaded(GBAThread*);
void postLog(int level, const QString& log);

View File

@ -50,7 +50,7 @@ void GamePakView::gameStarted(GBAThread* thread) {
}
SavedataType savetype = thread->gba->memory.savedata.type;
if (m_ui.savetype->currentIndex() > 0) {
if (savetype > SAVEDATA_NONE) {
if (savetype > SAVEDATA_FORCE_NONE) {
VFile* vf = thread->gba->memory.savedata.vf;
GBASavedataDeinit(&thread->gba->memory.savedata);
GBASavedataInit(&thread->gba->memory.savedata, vf);
@ -59,7 +59,7 @@ void GamePakView::gameStarted(GBAThread* thread) {
GBASavedataForceType(&thread->gba->memory.savedata, savetype);
}
if (savetype > SAVEDATA_NONE) {
if (savetype > SAVEDATA_AUTODETECT) {
m_ui.savetype->setCurrentIndex(savetype + 1);
}
m_ui.savetype->setEnabled(false);

View File

@ -27,9 +27,5 @@ qint64 VFileDevice::writeData(const char* data, qint64 maxSize) {
}
qint64 VFileDevice::size() const {
// TODO: Add size method to VFile so this can be actually const
ssize_t pos = m_vf->seek(m_vf, 0, SEEK_CUR);
qint64 size = m_vf->seek(m_vf, 0, SEEK_END);
m_vf->seek(m_vf, pos, SEEK_SET);
return size;
return m_vf->size(m_vf);
}

View File

@ -6,6 +6,7 @@
#include "Window.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QKeyEvent>
#include <QKeySequence>
#include <QMenuBar>
@ -55,6 +56,7 @@ Window::Window(ConfigController* config, QWidget* parent)
setFocusPolicy(Qt::StrongFocus);
m_controller = new GameController(this);
m_controller->setInputController(&m_inputController);
m_controller->setOverrides(m_config->overrides());
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
format.setSwapInterval(1);
@ -82,6 +84,7 @@ Window::Window(ConfigController* config, QWidget* parent)
connect(m_controller, SIGNAL(postLog(int, const QString&)), m_logView, SLOT(postLog(int, const QString&)));
connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(recordFrame()));
connect(m_controller, SIGNAL(gameCrashed(const QString&)), this, SLOT(gameCrashed(const QString&)));
connect(m_controller, SIGNAL(gameFailed()), this, SLOT(gameFailed()));
connect(m_logView, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int)));
connect(m_logView, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int)));
connect(m_logView, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int)));
@ -175,15 +178,17 @@ void Window::saveConfig() {
}
void Window::selectROM() {
QString filename = QFileDialog::getOpenFileName(this, tr("Select ROM"));
QString filename = QFileDialog::getOpenFileName(this, tr("Select ROM"), m_config->getQtOption("lastDirectory").toString(), tr("Game Boy Advance ROMs (*.gba *.zip *.rom *.bin)"));
if (!filename.isEmpty()) {
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
m_controller->loadGame(filename);
}
}
void Window::selectBIOS() {
QString filename = QFileDialog::getOpenFileName(this, tr("Select BIOS"));
QString filename = QFileDialog::getOpenFileName(this, tr("Select BIOS"), m_config->getQtOption("lastDirectory").toString());
if (!filename.isEmpty()) {
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
m_config->setOption("bios", filename);
m_config->updateOption("bios");
m_controller->loadBIOS(filename);
@ -191,8 +196,9 @@ void Window::selectBIOS() {
}
void Window::selectPatch() {
QString filename = QFileDialog::getOpenFileName(this, tr("Select patch"), QString(), tr("Patches (*.ips *.ups)"));
QString filename = QFileDialog::getOpenFileName(this, tr("Select patch"), m_config->getQtOption("lastDirectory").toString(), tr("Patches (*.ips *.ups)"));
if (!filename.isEmpty()) {
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
m_controller->loadPatch(filename);
}
}
@ -380,6 +386,14 @@ void Window::gameCrashed(const QString& errorMessage) {
crash->show();
}
void Window::gameFailed() {
QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Load"),
tr("Could not load game. Are you sure it's in the correct format?"),
QMessageBox::Ok, this, Qt::Sheet);
fail->setAttribute(Qt::WA_DeleteOnClose);
fail->show();
}
void Window::redoLogo() {
if (m_controller->isLoaded()) {
return;

View File

@ -96,6 +96,7 @@ private slots:
void gameStarted(GBAThread*);
void gameStopped();
void gameCrashed(const QString&);
void gameFailed();
void redoLogo();
void recordFrame();

View File

@ -103,6 +103,7 @@ int main(int argc, char** argv) {
GBASDLInitBindings(&inputMap);
GBASDLInitEvents(&renderer.events);
GBASDLEventsLoadConfig(&renderer.events, &config.configTable); // TODO: Don't use this directly
context.overrides = &config.configTable;
GBAThreadStart(&context);

View File

@ -213,7 +213,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
case SDLK_F8:
case SDLK_F9:
GBAThreadInterrupt(context);
GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
GBAThreadContinue(context);
break;
default:
@ -231,7 +231,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
case SDLK_F8:
case SDLK_F9:
GBAThreadInterrupt(context);
GBALoadState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1 + 1);
GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1);
GBAThreadContinue(context);
break;
default:

View File

@ -9,7 +9,7 @@
#include "util/vfs.h"
static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
static bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
static bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
bool loadPatchIPS(struct Patch* patch) {
patch->vf->seek(patch->vf, 0, SEEK_SET);
@ -42,10 +42,11 @@ size_t _IPSOutputSize(struct Patch* patch, size_t inSize) {
return inSize;
}
bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) {
return false;
}
memcpy(out, in, inSize > outSize ? outSize : inSize);
uint8_t* buf = out;
while (true) {

View File

@ -16,8 +16,11 @@ enum {
};
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
static bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
static size_t _UPSDecodeLength(struct VFile* vf);
static bool _UPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
static bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
static size_t _decodeLength(struct VFile* vf);
bool loadPatchUPS(struct Patch* patch) {
patch->vf->seek(patch->vf, 0, SEEK_SET);
@ -27,11 +30,15 @@ bool loadPatchUPS(struct Patch* patch) {
return false;
}
if (memcmp(buffer, "UPS1", 4) != 0) {
if (memcmp(buffer, "UPS1", 4) == 0) {
patch->applyPatch = _UPSApplyPatch;
} else if (memcmp(buffer, "BPS1", 4) == 0) {
patch->applyPatch = _BPSApplyPatch;
} else {
return false;
}
size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
size_t filesize = patch->vf->size(patch->vf);
uint32_t goodCrc32;
patch->vf->seek(patch->vf, PATCH_CHECKSUM, SEEK_END);
@ -45,34 +52,35 @@ bool loadPatchUPS(struct Patch* patch) {
}
patch->outputSize = _UPSOutputSize;
patch->applyPatch = _UPSApplyPatch;
return true;
}
size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
UNUSED(inSize);
patch->vf->seek(patch->vf, 4, SEEK_SET);
if (_UPSDecodeLength(patch->vf) != inSize) {
if (_decodeLength(patch->vf) != inSize) {
return 0;
}
return _UPSDecodeLength(patch->vf);
return _decodeLength(patch->vf);
}
bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
bool _UPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
// TODO: Input checksum
size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
size_t filesize = patch->vf->size(patch->vf);
patch->vf->seek(patch->vf, 4, SEEK_SET);
_UPSDecodeLength(patch->vf); // Discard input size
if (_UPSDecodeLength(patch->vf) != outSize) {
_decodeLength(patch->vf); // Discard input size
if (_decodeLength(patch->vf) != outSize) {
return false;
}
memcpy(out, in, inSize > outSize ? outSize : inSize);
size_t offset = 0;
size_t alreadyRead = 0;
uint8_t* buf = out;
while (alreadyRead < filesize + IN_CHECKSUM) {
offset += _UPSDecodeLength(patch->vf);
offset += _decodeLength(patch->vf);
uint8_t byte;
while (true) {
@ -101,7 +109,100 @@ bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
return true;
}
size_t _UPSDecodeLength(struct VFile* vf) {
bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
patch->vf->seek(patch->vf, IN_CHECKSUM, SEEK_END);
uint32_t expectedInChecksum;
uint32_t expectedOutChecksum;
patch->vf->read(patch->vf, &expectedInChecksum, sizeof(expectedInChecksum));
patch->vf->read(patch->vf, &expectedOutChecksum, sizeof(expectedOutChecksum));
uint32_t inputChecksum = doCrc32(in, inSize);
uint32_t outputChecksum = 0;
if (inputChecksum != expectedInChecksum) {
return false;
}
ssize_t filesize = patch->vf->size(patch->vf);
patch->vf->seek(patch->vf, 4, SEEK_SET);
_decodeLength(patch->vf); // Discard input size
if (_decodeLength(patch->vf) != outSize) {
return false;
}
size_t metadataLength = _decodeLength(patch->vf);
patch->vf->seek(patch->vf, metadataLength, SEEK_CUR); // Skip metadata
size_t writeLocation = 0;
ssize_t readSourceLocation = 0;
ssize_t readTargetLocation = 0;
size_t readOffset;
uint8_t* writeBuffer = out;
uint8_t* readBuffer = in;
while (patch->vf->seek(patch->vf, 0, SEEK_CUR) < filesize + IN_CHECKSUM) {
size_t command = _decodeLength(patch->vf);
size_t length = (command >> 2) + 1;
if (writeLocation + length > outSize) {
return false;
}
size_t i;
switch (command & 0x3) {
case 0x0:
// SourceRead
memmove(&writeBuffer[writeLocation], &readBuffer[writeLocation], length);
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
writeLocation += length;
break;
case 0x1:
// TargetRead
if (patch->vf->read(patch->vf, &writeBuffer[writeLocation], length) != length) {
return false;
}
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
writeLocation += length;
break;
case 0x2:
// SourceCopy
readOffset = _decodeLength(patch->vf);
if (readOffset & 1) {
readSourceLocation -= readOffset >> 1;
} else {
readSourceLocation += readOffset >> 1;
}
if (readSourceLocation < 0 || readSourceLocation > inSize) {
return false;
}
memmove(&writeBuffer[writeLocation], &readBuffer[readSourceLocation], length);
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
writeLocation += length;
readSourceLocation += length;
break;
case 0x3:
// TargetCopy
readOffset = _decodeLength(patch->vf);
if (readOffset & 1) {
readTargetLocation -= readOffset >> 1;
} else {
readTargetLocation += readOffset >> 1;
}
if (readTargetLocation < 0 || readTargetLocation > outSize) {
return false;
}
for (i = 0; i < length; ++i) {
// This needs to be bytewise as it can overlap
writeBuffer[writeLocation] = writeBuffer[readTargetLocation];
++writeLocation;
++readTargetLocation;
}
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation - length], length);
break;
}
}
if (expectedOutChecksum != outputChecksum) {
return false;
}
return true;
}
size_t _decodeLength(struct VFile* vf) {
size_t shift = 1;
size_t value = 0;
uint8_t byte;

View File

@ -14,7 +14,7 @@ struct Patch {
struct VFile* vf;
size_t (*outputSize)(struct Patch* patch, size_t inSize);
bool (*applyPatch)(struct Patch* patch, void* out, size_t outSize);
bool (*applyPatch)(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
};
bool loadPatch(struct VFile* vf, struct Patch* patch);

View File

@ -9,6 +9,7 @@
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <sys/mman.h>
@ -35,6 +36,7 @@ static ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size);
static void* _vfdMap(struct VFile* vf, size_t size, int flags);
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfdTruncate(struct VFile* vf, size_t size);
static ssize_t _vfdSize(struct VFile* vf);
static bool _vdClose(struct VDir* vd);
static void _vdRewind(struct VDir* vd);
@ -73,6 +75,7 @@ struct VFile* VFileFromFD(int fd) {
vfd->d.map = _vfdMap;
vfd->d.unmap = _vfdUnmap;
vfd->d.truncate = _vfdTruncate;
vfd->d.size = _vfdSize;
return &vfd->d;
}
@ -137,9 +140,12 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
createFlags = PAGE_READWRITE;
mapFiles = FILE_MAP_WRITE;
}
size_t location = lseek(vfd->fd, 0, SEEK_CUR);
size_t fileSize = lseek(vfd->fd, 0, SEEK_END);
lseek(vfd->fd, location, SEEK_SET);
size_t fileSize;
struct stat stat;
if (fstat(vfd->fd, &stat) < 0) {
return 0;
}
fileSize = stat.st_size;
if (size > fileSize) {
size = fileSize;
}
@ -161,6 +167,15 @@ static void _vfdTruncate(struct VFile* vf, size_t size) {
ftruncate(vfd->fd, size);
}
static ssize_t _vfdSize(struct VFile* vf) {
struct VFileFD* vfd = (struct VFileFD*) vf;
struct stat stat;
if (fstat(vfd->fd, &stat) < 0) {
return -1;
}
return stat.st_size;
}
struct VDirEntryDE {
struct VDirEntry d;
struct dirent* ent;

View File

@ -22,6 +22,7 @@ struct VFile {
void* (*map)(struct VFile* vf, size_t size, int flags);
void (*unmap)(struct VFile* vf, void* memory, size_t size);
void (*truncate)(struct VFile* vf, size_t size);
ssize_t (*size)(struct VFile* vf);
};
struct VDirEntry {

View File

@ -43,6 +43,7 @@ static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
static void* _vfzMap(struct VFile* vf, size_t size, int flags);
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfzTruncate(struct VFile* vf, size_t size);
static ssize_t _vfzSize(struct VFile* vf);
static bool _vdzClose(struct VDir* vd);
static void _vdzRewind(struct VDir* vd);
@ -229,6 +230,11 @@ void _vfzTruncate(struct VFile* vf, size_t size) {
UNUSED(size);
}
ssize_t _vfzSize(struct VFile* vf) {
struct VFileZip* vfz = (struct VFileZip*) vf;
return vfz->fileSize;
}
bool _vdzClose(struct VDir* vd) {
struct VDirZip* vdz = (struct VDirZip*) vd;
if (zip_close(vdz->z) < 0) {
@ -295,6 +301,7 @@ struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
vfz->d.map = _vfzMap;
vfz->d.unmap = _vfzUnmap;
vfz->d.truncate = _vfzTruncate;
vfz->d.size = _vfzSize;
return &vfz->d;
}