all repos — mgba @ dc5d1b40ee7b27fb441c9db743842407fc99248d

mGBA Game Boy Advance Emulator

Merge branch 'master' into optimization/idle-loops

Conflicts:
	src/gba/gba.c
Jeffrey Pfau jeffrey@endrift.com
Tue, 13 Jan 2015 01:54:59 -0800
commit

dc5d1b40ee7b27fb441c9db743842407fc99248d

parent

c937529d4ab5bb328923cc7654de5518ce8c111e

M CHANGESCHANGES

@@ -15,6 +15,9 @@ - Support for games using the tilt sensor

- Remappable shortcuts for keyboard and gamepad - Rewinding of emulation - Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter + - Support IPv6 + - Save directory of last loaded file + - Support BPS patches Bugfixes: - Qt: Fix issue with set frame sizes being the wrong height - Qt: Fix emulator crashing when full screen if a game is not running

@@ -46,6 +49,7 @@ - Qt: Fix a race condition when a game crashes immediately

- 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

@@ -55,6 +59,7 @@ - Qt: Set default log level to FATAL, ERROR and WARN

- Qt: Clarify some phrasing in the menus - GBA Memory: Implement 16- and 32-bit loads from SRAM - Qt: Clear active buttons when focus is lost + - GBA Memory: Simplify memory API and use fixed bus width 0.1.0: (2014-12-13) - Initial release
M src/arm/arm.hsrc/arm/arm.h

@@ -93,11 +93,9 @@ int32_t packed;

}; struct ARMMemory { - int32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter); - int16_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter); - uint16_t (*loadU16)(struct ARMCore*, uint32_t address, int* cycleCounter); - int8_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter); - uint8_t (*loadU8)(struct ARMCore*, uint32_t address, int* cycleCounter); + uint32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter); + uint32_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter); + uint32_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter); void (*store32)(struct ARMCore*, uint32_t address, int32_t value, int* cycleCounter); void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter);
M src/arm/decoder-arm.csrc/arm/decoder-arm.c

@@ -47,7 +47,7 @@

#define ADDR_MODE_1_IMM \ int rotate = (opcode & 0x00000F00) >> 7; \ int immediate = opcode & 0x000000FF; \ - info->op3.immediate = ARM_ROR(immediate, rotate); \ + info->op3.immediate = ROR(immediate, rotate); \ info->operandFormat |= ARM_OPERAND_IMMEDIATE_3; #define ADDR_MODE_2_SHIFT(OP) \

@@ -213,7 +213,6 @@ ARM_MEMORY_POST_INCREMENT | \

ARM_MEMORY_WRITEBACK, \ ADDRESSING_MODE, CYCLES, TYPE) \ DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## P, MNEMONIC, \ - ARM_MEMORY_PRE_INCREMENT | \ ARM_MEMORY_OFFSET_SUBTRACT, \ ADDRESSING_MODE, CYCLES, TYPE) \ DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PW, MNEMONIC, \

@@ -222,10 +221,9 @@ ARM_MEMORY_WRITEBACK | \

ARM_MEMORY_OFFSET_SUBTRACT, \ ADDRESSING_MODE, CYCLES, TYPE) \ DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PU, MNEMONIC, \ - ARM_MEMORY_PRE_INCREMENT, \ + 0, \ ADDRESSING_MODE, CYCLES, TYPE) \ DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PUW, MNEMONIC, \ - ARM_MEMORY_PRE_INCREMENT | \ ARM_MEMORY_WRITEBACK, \ ADDRESSING_MODE, CYCLES, TYPE)

@@ -243,10 +241,12 @@

#define DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \ DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \ ARM_MEMORY_POST_INCREMENT | \ + ARM_MEMORY_WRITEBACK | \ ARM_MEMORY_OFFSET_SUBTRACT, \ ADDRESSING_MODE, CYCLES, TYPE) \ DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \ - ARM_MEMORY_POST_INCREMENT, \ + ARM_MEMORY_POST_INCREMENT | \ + ARM_MEMORY_WRITEBACK, \ ADDRESSING_MODE, CYCLES, TYPE) #define DEFINE_LOAD_STORE_T_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \

@@ -416,7 +416,7 @@ ARM_OPERAND_REGISTER_2;)

DEFINE_DECODER_ARM(MSRI, MSR, info->affectsCPSR = 1; int rotate = (opcode & 0x00000F00) >> 7; - int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate); + int32_t operand = ROR(opcode & 0x000000FF, rotate); info->affectsCPSR = 1; info->op1.reg = ARM_CPSR; info->op2.immediate = operand;

@@ -426,7 +426,7 @@ ARM_OPERAND_IMMEDIATE_2;)

DEFINE_DECODER_ARM(MSRRI, MSR, info->affectsCPSR = 1; int rotate = (opcode & 0x00000F00) >> 7; - int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate); + int32_t operand = ROR(opcode & 0x000000FF, rotate); info->affectsCPSR = 1; info->op1.reg = ARM_SPSR; info->op2.immediate = operand;
M src/arm/isa-arm.csrc/arm/isa-arm.c

@@ -138,7 +138,7 @@ static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) {

int rm = opcode & 0x0000000F; int immediate = (opcode & 0x00000F80) >> 7; if (immediate) { - cpu->shifterOperand = ARM_ROR(cpu->gprs[rm], immediate); + cpu->shifterOperand = ROR(cpu->gprs[rm], immediate); cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1; } else { // RRX

@@ -165,7 +165,7 @@ if (!shift) {

cpu->shifterOperand = shiftVal; cpu->shifterCarryOut = cpu->cpsr.c; } else if (rotate) { - cpu->shifterOperand = ARM_ROR(shiftVal, rotate); + cpu->shifterOperand = ROR(shiftVal, rotate); cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1; } else { cpu->shifterOperand = shiftVal;

@@ -180,7 +180,7 @@ if (!rotate) {

cpu->shifterOperand = immediate; cpu->shifterCarryOut = cpu->cpsr.c; } else { - cpu->shifterOperand = ARM_ROR(immediate, rotate); + cpu->shifterOperand = ROR(immediate, rotate); cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand); } }

@@ -237,7 +237,7 @@ #define ADDR_MODE_2_WRITEBACK(ADDR) (cpu->gprs[rn] = ADDR)

#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I) #define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0) #define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31) -#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ARM_ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1)) +#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1)) #define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS #define ADDR_MODE_3_RN ADDR_MODE_2_RN

@@ -524,10 +524,10 @@

// Begin load/store definitions DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, cpu->gprs[rd] = cpu->memory.load32(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory.loadU16(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory.load8(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory.load16(cpu, address, &currentCycles); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, &currentCycles)); ARM_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = ARM_SXT_16(cpu->memory.load16(cpu, address, &currentCycles)); ARM_LOAD_POST_BODY;) DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory.store32(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory.store8(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;) DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory.store16(cpu, address, cpu->gprs[rd], &currentCycles); ARM_STORE_POST_BODY;)

@@ -535,7 +535,7 @@

DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT, enum PrivilegeMode priv = cpu->privilegeMode; ARMSetPrivilegeMode(cpu, MODE_USER); - cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, &currentCycles); + cpu->gprs[rd] = cpu->memory.load8(cpu, address, &currentCycles); ARMSetPrivilegeMode(cpu, priv); ARM_LOAD_POST_BODY;)

@@ -583,7 +583,7 @@ DEFINE_INSTRUCTION_ARM(SWPB,

int rm = opcode & 0xF; int rd = (opcode >> 12) & 0xF; int rn = (opcode >> 16) & 0xF; - int32_t d = cpu->memory.loadU8(cpu, cpu->gprs[rn], &currentCycles); + int32_t d = cpu->memory.load8(cpu, cpu->gprs[rn], &currentCycles); cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], &currentCycles); cpu->gprs[rd] = d;)

@@ -662,7 +662,7 @@ DEFINE_INSTRUCTION_ARM(MSRI,

int c = opcode & 0x00010000; int f = opcode & 0x00080000; int rotate = (opcode & 0x00000F00) >> 7; - int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate); + int32_t operand = ROR(opcode & 0x000000FF, rotate); int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0); if (mask & PSR_USER_MASK) { cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);

@@ -677,7 +677,7 @@ DEFINE_INSTRUCTION_ARM(MSRRI,

int c = opcode & 0x00010000; int f = opcode & 0x00080000; int rotate = (opcode & 0x00000F00) >> 7; - int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate); + int32_t operand = ROR(opcode & 0x000000FF, rotate); int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0); mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK; cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask);)
M src/arm/isa-inlines.hsrc/arm/isa-inlines.h

@@ -27,8 +27,8 @@ #define ARM_COND_LE (cpu->cpsr.z || !cpu->cpsr.n != !cpu->cpsr.v)

#define ARM_COND_AL 1 #define ARM_SIGN(I) ((I) >> 31) -#define ARM_ROR(I, ROTATE) ((((uint32_t) (I)) >> ROTATE) | ((uint32_t) (I) << ((-ROTATE) & 31))) - +#define ARM_SXT_8(I) (((int8_t) (I) << 24) >> 24) +#define ARM_SXT_16(I) (((int16_t) (I) << 16) >> 16) #define ARM_CARRY_FROM(M, N, D) (((uint32_t) (M) >> 31) + ((uint32_t) (N) >> 31) > ((uint32_t) (D) >> 31)) #define ARM_BORROW_FROM(M, N, D) (((uint32_t) (M)) >= ((uint32_t) (N)))
M src/arm/isa-thumb.csrc/arm/isa-thumb.c

@@ -97,8 +97,8 @@ }

THUMB_NEUTRAL_S( , , cpu->gprs[rd]);) DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDR1, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rm] + immediate * 4, &currentCycles); THUMB_LOAD_POST_BODY;) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rm] + immediate, &currentCycles); THUMB_LOAD_POST_BODY;) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rm] + immediate * 2, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rm] + immediate, &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rm] + immediate * 2, &currentCycles); THUMB_LOAD_POST_BODY;) DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory.store32(cpu, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory.store8(cpu, cpu->gprs[rm] + immediate, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;)

@@ -219,7 +219,7 @@ if (rs) {

int r4 = rs & 0x1F; if (r4 > 0) { cpu->cpsr.c = (cpu->gprs[rd] >> (r4 - 1)) & 1; - cpu->gprs[rd] = ARM_ROR(cpu->gprs[rd], r4); + cpu->gprs[rd] = ROR(cpu->gprs[rd], r4); } else { cpu->cpsr.c = ARM_SIGN(cpu->gprs[rd]); }

@@ -286,10 +286,10 @@ #define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, BODY) \

COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) -DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)); THUMB_LOAD_POST_BODY;) +DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = ARM_SXT_16(cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles)); THUMB_LOAD_POST_BODY;) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory.store32(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory.store8(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); THUMB_STORE_POST_BODY;)
M src/arm/macros.hsrc/arm/macros.h

@@ -69,4 +69,7 @@ #define DECL_BIT(TYPE, FIELD, BIT) DECL_BITS(TYPE, FIELD, BIT, 1)

#define LIKELY(X) __builtin_expect(!!(X), 1) #define UNLIKELY(X) __builtin_expect(!!(X), 0) + +#define ROR(I, ROTATE) ((((uint32_t) (I)) >> ROTATE) | ((uint32_t) (I) << ((-ROTATE) & 31))) + #endif
M src/debugger/cli-debugger.csrc/debugger/cli-debugger.c

@@ -248,7 +248,7 @@ ARMDecodeARM(instruction, &info);

ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly)); printf("%08X\t%s\n", instruction, disassembly); } else { - uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0); + uint16_t instruction = debugger->d.cpu->memory.load16(debugger->d.cpu, address, 0); ARMDecodeThumb(instruction, &info); ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly)); printf("%04X\t%s\n", instruction, disassembly);

@@ -287,7 +287,7 @@ printf("%s\n", ERROR_MISSING_ARGS);

return; } uint32_t address = dv->intValue; - uint8_t value = debugger->d.cpu->memory.loadU8(debugger->d.cpu, address, 0); + uint8_t value = debugger->d.cpu->memory.load8(debugger->d.cpu, address, 0); printf(" 0x%02X\n", value); }

@@ -303,7 +303,7 @@ printf("%s\n", ERROR_MISSING_ARGS);

return; } uint32_t address = dv->intValue; - uint16_t value = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0); + uint16_t value = debugger->d.cpu->memory.load16(debugger->d.cpu, address & ~1, 0); printf(" 0x%04X\n", value); }

@@ -313,7 +313,7 @@ printf("%s\n", ERROR_MISSING_ARGS);

return; } uint32_t address = dv->intValue; - uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0); + uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address & ~3, 0); printf(" 0x%08X\n", value); }
M src/debugger/gdb-stub.csrc/debugger/gdb-stub.c

@@ -446,11 +446,10 @@ stub->d.entered = _gdbStubEntered;

stub->d.log = 0; } -int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) { +int GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) { if (!SOCKET_FAILED(stub->socket)) { GDBStubShutdown(stub); } - // TODO: support IPv6 stub->socket = SocketOpenTCP(port, bindAddress); if (SOCKET_FAILED(stub->socket)) { if (stub->d.log) {

@@ -500,7 +499,7 @@ if (stub->socket == INVALID_SOCKET) {

return; } if (stub->connection == INVALID_SOCKET) { - stub->connection = SocketAccept(stub->socket, 0, 0); + stub->connection = SocketAccept(stub->socket, 0); if (!SOCKET_FAILED(stub->connection)) { if (!SocketSetBlocking(stub->connection, 0)) { goto connectionLost;
M src/debugger/gdb-stub.hsrc/debugger/gdb-stub.h

@@ -33,7 +33,7 @@ Socket connection;

}; void GDBStubCreate(struct GDBStub*); -int GDBStubListen(struct GDBStub*, int port, uint32_t bindAddress); +int GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress); void GDBStubHangup(struct GDBStub*); void GDBStubShutdown(struct GDBStub*);
M src/debugger/memory-debugger.csrc/debugger/memory-debugger.c

@@ -40,11 +40,9 @@ } \

return debugger->originalMemory.NAME(cpu, ARGS); \ } -CREATE_WATCHPOINT_SHIM(load32, 4, int32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_WATCHPOINT_SHIM(load16, 2, int16_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_WATCHPOINT_SHIM(loadU16, 2, uint16_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_WATCHPOINT_SHIM(load8, 1, int8_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) -CREATE_WATCHPOINT_SHIM(loadU8, 1, uint8_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(load32, 4, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(load16, 2, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) +CREATE_WATCHPOINT_SHIM(load8, 1, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter) CREATE_WATCHPOINT_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter) CREATE_WATCHPOINT_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter) CREATE_WATCHPOINT_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter)

@@ -67,9 +65,7 @@ debugger->cpu->memory.store16 = ARMDebuggerShim_store16;

debugger->cpu->memory.store8 = ARMDebuggerShim_store8; debugger->cpu->memory.load32 = ARMDebuggerShim_load32; debugger->cpu->memory.load16 = ARMDebuggerShim_load16; - debugger->cpu->memory.loadU16 = ARMDebuggerShim_loadU16; debugger->cpu->memory.load8 = ARMDebuggerShim_load8; - debugger->cpu->memory.loadU8 = ARMDebuggerShim_loadU8; debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion; }

@@ -79,8 +75,6 @@ debugger->cpu->memory.store16 = debugger->originalMemory.store16;

debugger->cpu->memory.store8 = debugger->originalMemory.store8; debugger->cpu->memory.load32 = debugger->originalMemory.load32; debugger->cpu->memory.load16 = debugger->originalMemory.load16; - debugger->cpu->memory.loadU16 = debugger->originalMemory.loadU16; debugger->cpu->memory.load8 = debugger->originalMemory.load8; - debugger->cpu->memory.loadU8 = debugger->originalMemory.loadU8; debugger->cpu->memory.setActiveRegion = debugger->originalMemory.setActiveRegion; }
M src/gba/gba-bios.csrc/gba/gba-bios.c

@@ -94,7 +94,7 @@ cx = cpu->memory.load16(cpu, offset + 8, 0);

cy = cpu->memory.load16(cpu, offset + 10, 0); sx = cpu->memory.load16(cpu, offset + 12, 0) / 256.f; sy = cpu->memory.load16(cpu, offset + 14, 0) / 256.f; - theta = (cpu->memory.loadU16(cpu, offset + 16, 0) >> 8) / 128.f * M_PI; + theta = (cpu->memory.load16(cpu, offset + 16, 0) >> 8) / 128.f * M_PI; offset += 20; // Rotation a = d = cosf(theta);

@@ -131,7 +131,7 @@ // [ sx 0 ] [ cos(theta) -sin(theta) ] [ A B ]

// [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ] sx = cpu->memory.load16(cpu, offset, 0) / 256.f; sy = cpu->memory.load16(cpu, offset + 2, 0) / 256.f; - theta = (cpu->memory.loadU16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI; + theta = (cpu->memory.load16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI; offset += 8; // Rotation a = d = cosf(theta);

@@ -312,7 +312,6 @@ // We assume the signature byte (0x10) is correct

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,29 +320,32 @@ while (remaining > 0) {

if (blocksRemaining) { if (blockheader & 0x80) { // Compressed - block = cpu->memory.loadU8(cpu, source, 0) | (cpu->memory.loadU8(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.loadU8(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 { // Uncompressed - byte = cpu->memory.loadU8(cpu, source, 0); + byte = cpu->memory.load8(cpu, source, 0); ++source; if (width == 2) { if (dest & 1) {

@@ -361,7 +363,7 @@ }

blockheader <<= 1; --blocksRemaining; } else { - blockheader = cpu->memory.loadU8(cpu, source, 0); + blockheader = cpu->memory.load8(cpu, source, 0); ++source; blocksRemaining = 8; }

@@ -390,7 +392,7 @@ }

int padding = (4 - remaining) & 0x3; remaining &= 0xFFFFFFFC; // We assume the signature byte (0x20) is correct - int treesize = (cpu->memory.loadU8(cpu, source + 4, 0) << 1) + 1; + int treesize = (cpu->memory.load8(cpu, source + 4, 0) << 1) + 1; int block = 0; uint32_t treeBase = source + 5; source += 5 + treesize;

@@ -458,13 +460,13 @@ source += 4;

uint32_t dest = cpu->gprs[1]; int halfword = 0; while (remaining > 0) { - blockheader = cpu->memory.loadU8(cpu, source, 0); + blockheader = cpu->memory.load8(cpu, source, 0); ++source; if (blockheader & 0x80) { // Compressed blockheader &= 0x7F; blockheader += 3; - block = cpu->memory.loadU8(cpu, source, 0); + block = cpu->memory.load8(cpu, source, 0); ++source; while (blockheader-- && remaining) { --remaining;

@@ -485,7 +487,7 @@ // Uncompressed

blockheader++; while (blockheader-- && remaining) { --remaining; - int byte = cpu->memory.loadU8(cpu, source, 0); + int byte = cpu->memory.load8(cpu, source, 0); ++source; if (width == 2) { if (dest & 1) {

@@ -532,9 +534,9 @@ source += 4;

while (remaining > 0) { uint16_t new; if (inwidth == 1) { - new = cpu->memory.loadU8(cpu, source, 0); + new = cpu->memory.load8(cpu, source, 0); } else { - new = cpu->memory.loadU16(cpu, source, 0); + new = cpu->memory.load16(cpu, source, 0); } new += old; if (outwidth > inwidth) {
M src/gba/gba-cli.csrc/gba/gba-cli.c

@@ -112,7 +112,7 @@ }

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

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
M src/gba/gba-memory.csrc/gba/gba-memory.c

@@ -31,9 +31,7 @@ void GBAMemoryInit(struct GBA* gba) {

struct ARMCore* cpu = gba->cpu; cpu->memory.load32 = GBALoad32; cpu->memory.load16 = GBALoad16; - cpu->memory.loadU16 = GBALoadU16; cpu->memory.load8 = GBALoad8; - cpu->memory.loadU8 = GBALoadU8; cpu->memory.loadMultiple = GBALoadMultiple; cpu->memory.store32 = GBAStore32; cpu->memory.store16 = GBAStore16;

@@ -227,7 +225,7 @@ value = GBALoad8(cpu, address, 0); \

value |= value << 8; \ value |= value << 16; -int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { +uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; uint32_t value = 0;

@@ -278,17 +276,13 @@ *cycleCounter += 2 + wait;

} // Unaligned 32-bit loads are "rotated" so they make some semblance of sense int rotate = (address & 3) << 3; - return (value >> rotate) | (value << (32 - rotate)); -} - -uint16_t GBALoadU16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { - return GBALoad16(cpu, address, cycleCounter); + return ROR(value, rotate); } -int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { +uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; - uint16_t value = 0; + uint32_t value = 0; int wait = 0; switch (address >> BASE_OFFSET) {

@@ -298,14 +292,14 @@ if (memory->activeRegion == REGION_BIOS) {

LOAD_16(value, address, memory->bios); } else { GBALog(gba, GBA_LOG_GAME_ERROR, "Bad BIOS Load16: 0x%08X", address); - value = memory->biosPrefetch; + value = memory->biosPrefetch & 0xFFFF; } } else { GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address); if (cpu->cycles >= cpu->nextEvent) { - value = gba->bus; + value = gba->bus & 0xFFFF; } else { - value = cpu->prefetch[1]; + value = cpu->prefetch[1] & 0xFFFF; } } break;

@@ -373,17 +367,13 @@ *cycleCounter += 2 + wait;

} // Unaligned 16-bit loads are "unpredictable", but the GBA rotates them, so we have to, too. int rotate = (address & 1) << 3; - return (value >> rotate) | (value << (16 - rotate)); -} - -uint8_t GBALoadU8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { - return GBALoad8(cpu, address, cycleCounter); + return ROR(value, rotate); } -int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { +uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; - int8_t value = 0; + uint8_t value = 0; int wait = 0; switch (address >> BASE_OFFSET) {

@@ -440,7 +430,7 @@ break;

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

@@ -596,7 +586,7 @@ GBALog(gba, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);

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

@@ -652,7 +642,7 @@ GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Store8: 0x%08X", address);

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

@@ -1152,7 +1142,7 @@ source += sourceOffset;

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); }
M src/gba/gba-memory.hsrc/gba/gba-memory.h

@@ -144,11 +144,9 @@ void GBAMemoryDeinit(struct GBA* gba);

void GBAMemoryReset(struct GBA* gba); -int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter); -int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); -uint16_t GBALoadU16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); -int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); -uint8_t GBALoadU8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter); +uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter); void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter); void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter);
A src/gba/gba-overrides.c

@@ -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; +}
A src/gba/gba-overrides.h

@@ -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
M src/gba/gba-savedata.csrc/gba/gba-savedata.c

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

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

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 @@ case SAVEDATA_FLASH1M:

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

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 @@ if (!savedata->vf) {

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 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 @@ if (!savedata->vf) {

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 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 @@ if (!savedata->vf) {

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); }
M src/gba/gba-savedata.hsrc/gba/gba-savedata.h

@@ -11,7 +11,8 @@

struct VFile; enum SavedataType { - SAVEDATA_NONE = 0, + SAVEDATA_AUTODETECT = -1, + SAVEDATA_FORCE_NONE = 0, SAVEDATA_SRAM, SAVEDATA_FLASH512, SAVEDATA_FLASH1M,
M src/gba/gba-serialize.csrc/gba/gba-serialize.c

@@ -175,22 +175,23 @@ return true;

} #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; }
M src/gba/gba-serialize.hsrc/gba/gba-serialize.h

@@ -282,12 +282,13 @@ uint8_t wram[SIZE_WORKING_RAM];

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

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);
M src/gba/gba-sio.csrc/gba/gba-sio.c

@@ -7,6 +7,13 @@ #include "gba-sio.h"

#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:
M src/gba/gba-sio.hsrc/gba/gba-sio.h

@@ -8,6 +8,10 @@ #define GBA_SIO_H

#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,
M src/gba/gba-thread.csrc/gba/gba-thread.c

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

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); }
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -70,6 +70,7 @@ struct VFile* patch;

const char* fname; int activeKeys; struct GBAAVStream* stream; + struct Configuration* overrides; // Run-time options int frameskip;
M src/gba/gba.csrc/gba/gba.c

@@ -24,119 +24,12 @@

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 }, - - // Golden Sun: The Lost Age - { "AGFE", SAVEDATA_FLASH512, GPIO_NONE, 0x801353A }, - - // Koro Koro Puzzle - Happy Panechu! - { "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 }, - - // Mega Man Battle Network - { "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E }, - - // Metal Slug Advance - { "BSME", SAVEDATA_EEPROM, GPIO_NONE, 0x8000290 }, - - // 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, 0x80008C6 }, - { "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 2 - { "AA2E", SAVEDATA_EEPROM, GPIO_NONE, 0x800052E }, - - // Super Mario Advance 3 - { "A3AE", SAVEDATA_EEPROM, GPIO_NONE, 0x8002B9C }, - - // 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); 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;

@@ -452,7 +345,7 @@ }

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;

@@ -468,7 +361,6 @@ gba->memory.romSize = gba->pristineRomSize;

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 }

@@ -503,8 +395,7 @@ if (!patchedSize) {

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;

@@ -744,43 +635,3 @@ if (gba->debugger) {

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; - } - } -}
M src/platform/perf-main.csrc/platform/perf-main.c

@@ -76,6 +76,7 @@ context.renderer = &renderer.d;

} context.debugger = createDebugger(&args, &context); + context.overrides = &config.configTable; char gameCode[5] = { 0 }; GBAConfigMap(&config, &opts);

@@ -121,7 +122,7 @@ } else {

printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f)); } - return 0; + return GBAThreadHasCrashed(&context); } static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet) {
M src/platform/qt/ConfigController.hsrc/platform/qt/ConfigController.h

@@ -75,6 +75,8 @@

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);
M src/platform/qt/GBAApp.cppsrc/platform/qt/GBAApp.cpp

@@ -26,15 +26,15 @@

QApplication::setApplicationName(PROJECT_NAME); QApplication::setApplicationVersion(PROJECT_VERSION); + m_window.show(); + GBAArguments args = {}; if (m_configController.parseArguments(&args, argc, argv)) { - m_window.argumentsPassed(&args); + m_window.argumentsPassed(&args); } else { - m_window.loadConfig(); + m_window.loadConfig(); } freeArguments(&args); - - m_window.show(); } bool GBAApp::event(QEvent* event) {
M src/platform/qt/GDBController.cppsrc/platform/qt/GDBController.cpp

@@ -13,7 +13,7 @@ GDBController::GDBController(GameController* controller, QObject* parent)

: QObject(parent) , m_gameController(controller) , m_port(2345) - , m_bindAddress(0) + , m_bindAddress({ IPV4, 0 }) { GDBStubCreate(&m_gdbStub); }

@@ -22,10 +22,6 @@ ushort GDBController::port() {

return m_port; } -uint32_t GDBController::bindAddress() { - return m_bindAddress; -} - bool GDBController::isAttached() { return m_gameController->debugger() == &m_gdbStub.d; }

@@ -35,7 +31,8 @@ m_port = port;

} void GDBController::setBindAddress(uint32_t bindAddress) { - m_bindAddress = bindAddress; + m_bindAddress.version = IPV4; + m_bindAddress.ipv4 = htonl(bindAddress); } void GDBController::attach() {

@@ -62,7 +59,7 @@ if (!isAttached()) {

attach(); } connect(m_gameController, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateGDB())); - GDBStubListen(&m_gdbStub, m_port, m_bindAddress); + GDBStubListen(&m_gdbStub, m_port, &m_bindAddress); m_gameController->threadContinue(); }
M src/platform/qt/GDBController.hsrc/platform/qt/GDBController.h

@@ -26,7 +26,6 @@ GDBController(GameController* controller, QObject* parent = nullptr);

public: ushort port(); - uint32_t bindAddress(); bool isAttached(); public slots:

@@ -44,7 +43,7 @@ GDBStub m_gdbStub;

GameController* m_gameController; ushort m_port; - uint32_t m_bindAddress; + Address m_bindAddress; }; }
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -204,6 +204,7 @@ }

if (!GBAThreadStart(&m_threadContext)) { m_gameOpen = false; + emit gameFailed(); } }

@@ -360,7 +361,7 @@ }

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::saveState(int slot) { threadInterrupt(); - GBASaveState(m_threadContext.gba, m_threadContext.stateDir, slot, true); + GBASaveState(&m_threadContext, m_threadContext.stateDir, slot, true); threadContinue(); }
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -22,6 +22,7 @@ }

struct GBAAudio; struct GBAVideoSoftwareRenderer; +struct Configuration; class QThread;

@@ -53,6 +54,7 @@ bool audioSync() const { return m_audioSync; }

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 @@ void gameStopped(GBAThread*);

void gamePaused(GBAThread*); void gameUnpaused(GBAThread*); void gameCrashed(const QString& errorMessage); + void gameFailed(); void stateLoaded(GBAThread*); void postLog(int level, const QString& log);
M src/platform/qt/GamePakView.cppsrc/platform/qt/GamePakView.cpp

@@ -50,7 +50,7 @@ return;

} 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 @@ savetype = static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1);

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);
M src/platform/qt/VFileDevice.cppsrc/platform/qt/VFileDevice.cpp

@@ -27,9 +27,5 @@ return m_vf->write(m_vf, data, 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); }
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Window.h" #include <QFileDialog> +#include <QFileInfo> #include <QKeyEvent> #include <QKeySequence> #include <QMenuBar>

@@ -55,6 +56,7 @@ setWindowTitle(PROJECT_NAME);

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 @@ connect(m_controller, SIGNAL(gameUnpaused(GBAThread*)), m_display, SLOT(unpauseDrawing()));

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

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

@@ -378,6 +384,14 @@ tr("The game has crashed with the following error:\n\n%1").arg(errorMessage),

QMessageBox::Ok, this, Qt::Sheet); crash->setAttribute(Qt::WA_DeleteOnClose); 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() {
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -96,6 +96,7 @@ private slots:

void gameStarted(GBAThread*); void gameStopped(); void gameCrashed(const QString&); + void gameFailed(); void redoLogo(); void recordFrame();
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -103,6 +103,7 @@ renderer.events.bindings = &inputMap;

GBASDLInitBindings(&inputMap); GBASDLInitEvents(&renderer.events); GBASDLEventsLoadConfig(&renderer.events, &config.configTable); // TODO: Don't use this directly + context.overrides = &config.configTable; GBAThreadStart(&context);
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -213,7 +213,7 @@ case SDLK_F7:

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 @@ case SDLK_F7:

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:
M src/util/patch-ips.csrc/util/patch-ips.c

@@ -9,7 +9,7 @@ #include "util/patch.h"

#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 @@ UNUSED(patch);

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) {
M src/util/patch-ups.csrc/util/patch-ups.c

@@ -16,8 +16,11 @@ PATCH_CHECKSUM = -4,

}; 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 @@ if (patch->vf->read(patch->vf, buffer, 4) != 4) {

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

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

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;
M src/util/patch.hsrc/util/patch.h

@@ -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);
M src/util/socket.hsrc/util/socket.h

@@ -29,6 +29,18 @@ #define SOCKET_FAILED(s) (s) < 0

typedef int Socket; #endif +enum IP { + IPV4, + IPV6 +}; + +struct Address { + enum IP version; + union { + uint32_t ipv4; + uint8_t ipv6[16]; + }; +}; static inline void SocketSubsystemInitialize() { #ifdef _WIN32

@@ -44,18 +56,35 @@ static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {

return read(socket, buffer, size); } -static inline Socket SocketOpenTCP(int port, uint32_t bindAddress) { +static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) { Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (SOCKET_FAILED(sock)) { return sock; } - struct sockaddr_in bindInfo; - memset(&bindInfo, 0, sizeof(bindInfo)); - bindInfo.sin_family = AF_INET; - bindInfo.sin_port = htons(port); - bindInfo.sin_addr.s_addr = htonl(bindAddress); - int err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in)); + int err; + if (!bindAddress) { + struct sockaddr_in bindInfo; + memset(&bindInfo, 0, sizeof(bindInfo)); + bindInfo.sin_family = AF_INET; + bindInfo.sin_port = htons(port); + err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); + } else if (bindAddress->version == IPV4) { + struct sockaddr_in bindInfo; + memset(&bindInfo, 0, sizeof(bindInfo)); + bindInfo.sin_family = AF_INET; + bindInfo.sin_port = htons(port); + bindInfo.sin_addr.s_addr = bindAddress->ipv4; + err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); + } else { + struct sockaddr_in6 bindInfo; + memset(&bindInfo, 0, sizeof(bindInfo)); + bindInfo.sin6_family = AF_INET6; + bindInfo.sin6_port = htons(port); + memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr)); + err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); + + } if (err) { close(sock); return -1;

@@ -63,18 +92,35 @@ }

return sock; } -static inline Socket SocketConnectTCP(int port, uint32_t destinationAddress) { +static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) { Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (SOCKET_FAILED(sock)) { return sock; } - struct sockaddr_in bindInfo; - memset(&bindInfo, 0, sizeof(bindInfo)); - bindInfo.sin_family = AF_INET; - bindInfo.sin_port = htons(port); - bindInfo.sin_addr.s_addr = htonl(destinationAddress); - int err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in)); + int err; + if (!destinationAddress) { + struct sockaddr_in bindInfo; + memset(&bindInfo, 0, sizeof(bindInfo)); + bindInfo.sin_family = AF_INET; + bindInfo.sin_port = htons(port); + err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); + } else if (destinationAddress->version == IPV4) { + struct sockaddr_in bindInfo; + memset(&bindInfo, 0, sizeof(bindInfo)); + bindInfo.sin_family = AF_INET; + bindInfo.sin_port = htons(port); + bindInfo.sin_addr.s_addr = destinationAddress->ipv4; + err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); + } else { + struct sockaddr_in6 bindInfo; + memset(&bindInfo, 0, sizeof(bindInfo)); + bindInfo.sin6_family = AF_INET6; + bindInfo.sin6_port = htons(port); + memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr)); + err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); + } + if (err) { close(sock); return -1;

@@ -86,8 +132,25 @@ static inline Socket SocketListen(Socket socket, int queueLength) {

return listen(socket, queueLength); } -static inline Socket SocketAccept(Socket socket, struct sockaddr* restrict address, socklen_t* restrict addressLength) { - return accept(socket, address, addressLength); +static inline Socket SocketAccept(Socket socket, struct Address* address) { + if (!address) { + return accept(socket, 0, 0); + } + if (address->version == IPV4) { + struct sockaddr_in addrInfo; + memset(&addrInfo, 0, sizeof(addrInfo)); + addrInfo.sin_family = AF_INET; + addrInfo.sin_addr.s_addr = address->ipv4; + socklen_t len = sizeof(addrInfo); + return accept(socket, (struct sockaddr*) &addrInfo, &len); + } else { + struct sockaddr_in6 addrInfo; + memset(&addrInfo, 0, sizeof(addrInfo)); + addrInfo.sin6_family = AF_INET6; + memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr)); + socklen_t len = sizeof(addrInfo); + return accept(socket, (struct sockaddr*) &addrInfo, &len); + } } static inline int SocketClose(Socket socket) {
M src/util/vfs.csrc/util/vfs.c

@@ -9,6 +9,7 @@ #include "util/string.h"

#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 @@ vfd->d.write = _vfdWrite;

vfd->d.map = _vfdMap; vfd->d.unmap = _vfdUnmap; vfd->d.truncate = _vfdTruncate; + vfd->d.size = _vfdSize; return &vfd->d; }

@@ -137,9 +140,12 @@ if (flags & MAP_WRITE) {

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

@@ -159,6 +165,15 @@

static void _vfdTruncate(struct VFile* vf, size_t size) { struct VFileFD* vfd = (struct VFileFD*) vf; 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 {
M src/util/vfs.hsrc/util/vfs.h

@@ -22,6 +22,7 @@ ssize_t (*write)(struct VFile* vf, const void* buffer, size_t size);

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 {
M src/util/vfs/vfs-zip.csrc/util/vfs/vfs-zip.c

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

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 @@ vfz->d.write = _vfzWrite;

vfz->d.map = _vfzMap; vfz->d.unmap = _vfzUnmap; vfz->d.truncate = _vfzTruncate; + vfz->d.size = _vfzSize; return &vfz->d; }
M tools/perf.pytools/perf.py

@@ -40,7 +40,7 @@ proc.wait()

except: proc.kill() raise - if proc.returncode < 0: + if proc.returncode: print('Game crashed!', file=sys.stderr) return reader = csv.DictReader(proc.stdout)