Merge branch 'master' into feature/sio-lockstep

Conflicts:
	CMakeLists.txt
	src/gba/gba.c
This commit is contained in:
Jeffrey Pfau 2015-03-15 23:29:01 -07:00
commit 51b8c862b9
62 changed files with 3831 additions and 959 deletions

View File

@ -26,6 +26,8 @@ Features:
- Debugger: Add CLI functions for examining memory regions
- Runtime configurable audio driver
- Debugger: Add CLI function for writing a register
- Libretro core for use with RetroArch and other front-ends
- Controller profiles for setting different bindings for different controllers
Bugfixes:
- ARM7: Extend prefetch by one stage
- GBA Audio: Support 16-bit writes to FIFO audio
@ -46,6 +48,9 @@ Bugfixes:
- Qt: Fix crash when starting GDB stub after closing a game
- Qt: Fix patch loading while a game is running
- Util: Fix sockets on Windows
- Qt: Fix crash when loading a game after stopping GDB server
- GBA BIOS: Fix BIOS decompression routines with invalid source addresses
- GBA: Initialize gba.sync to null
Misc:
- GBA Audio: Change internal audio sample buffer from 32-bit to 16-bit samples
- GBA Memory: Simplify memory API and use fixed bus width
@ -68,6 +73,7 @@ Misc:
- Qt: Move frame upload back onto main thread
- All: Enable link-time optimization
- GBA Thread: Make GBASyncWaitFrameStart time out
- GBA: Move A/V stream interface into core
0.1.1: (2015-01-24)
Bugfixes:

View File

@ -12,11 +12,13 @@ set(USE_BLIP ON CACHE BOOL "Whether or not to enable blip_buf support")
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
set(BUILD_QT ON CACHE BOOL "Build Qt frontend")
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core")
set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")
set(BUILD_STATIC OFF CACHE BOOL "Build a static library")
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c)
file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c)
file(GLOB GBA_RR_SRC ${CMAKE_SOURCE_DIR}/src/gba/rr/*.c)
file(GLOB GBA_SV_SRC ${CMAKE_SOURCE_DIR}/src/gba/supervisor/*.c)
file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.[cSs])
file(GLOB VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/*.c)
@ -26,7 +28,7 @@ file(GLOB THIRD_PARTY_SRC ${CMAKE_SOURCE_DIR}/src/third-party/inih/*.c)
list(APPEND UTIL_SRC ${CMAKE_SOURCE_DIR}/src/platform/commandline.c)
source_group("ARM core" FILES ${ARM_SRC})
source_group("GBA board" FILES ${GBA_SRC} ${RENDERER_SRC} ${SIO_SRC})
source_group("GBA supervisor" FILES ${GBA_SV_SRC})
source_group("GBA supervisor" FILES ${GBA_SV_SRC} ${GBA_RR_SRC})
source_group("Utilities" FILES ${UTIL_SRC} ${VFS_SRC}})
include_directories(${CMAKE_SOURCE_DIR}/src/arm)
include_directories(${CMAKE_SOURCE_DIR}/src)
@ -75,10 +77,11 @@ set(LIB_VERSION_ABI 0.2)
set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH})
# Advanced settings
set(BUILD_PGO CACHE BOOL "Build with profiling-guided optimization")
set(BUILD_LTO ON CACHE BOOL "Build with link-time optimization")
set(BUILD_PGO OFF CACHE BOOL "Build with profiling-guided optimization")
set(PGO_STAGE_2 CACHE BOOL "Rebuild for profiling-guided optimization after profiles have been generated")
set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path")
mark_as_advanced(BUILD_PGO PGO_STAGE_2 PGO_DIR)
mark_as_advanced(BUILD_LTO BUILD_PGO PGO_STAGE_2 PGO_DIR)
set(PGO_PRE_FLAGS "-pg -fprofile-generate=${PGO_DIR} -fprofile-arcs")
set(PGO_POST_FLAGS "-fprofile-use=${PGO_DIR} -fbranch-probabilities")
@ -95,6 +98,7 @@ endif()
add_definitions(-DBINARY_NAME="${BINARY_NAME}" -DPROJECT_NAME="${PROJECT_NAME}" -DPROJECT_VERSION="${LIB_VERSION_STRING}")
# Feature dependencies
set(FEATURES)
if(CMAKE_SYSTEM_NAME MATCHES .*BSD)
set(LIBEDIT_LIBRARIES -ledit)
else()
@ -124,18 +128,21 @@ if(APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6")
endif()
if(APPLE OR CMAKE_C_COMPILER_ID STREQUAL "GNU")
if(APPLE OR CMAKE_C_COMPILER_ID STREQUAL "GNU" AND BUILD_LTO)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto")
endif()
if(BUILD_BBB OR BUILD_RASPI)
enable_language(ASM)
if(NOT BUILD_EGL)
add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5)
endif()
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*")
enable_language(ASM)
endif()
include(CheckFunctionExists)
check_function_exists(strndup HAVE_STRNDUP)
check_function_exists(snprintf_l HAVE_SNPRINTF_L)
@ -166,13 +173,14 @@ endif()
# Features
set(DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/debugger.c ${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c)
set(FEATURE_SRC)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6")
if(USE_CLI_DEBUGGER)
add_definitions(-DUSE_CLI_DEBUGGER)
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c)
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c)
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/gba/supervisor/cli.c)
list(APPEND FEATURES CLI_DEBUGGER)
list(APPEND FEATURE_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c)
list(APPEND FEATURE_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c)
list(APPEND FEATURE_SRC ${CMAKE_SOURCE_DIR}/src/gba/supervisor/cli.c)
include_directories(AFTER ${LIBEDIT_INCLUDE_DIRS})
link_directories(${LIBEDIT_LIBRARY_DIRS})
set(DEBUGGER_LIB ${LIBEDIT_LIBRARIES})
@ -182,20 +190,20 @@ else()
endif()
if(USE_GDB_STUB)
add_definitions(-DUSE_GDB_STUB)
list(APPEND FEATURES GDB_STUB)
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/gdb-stub.c)
endif()
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
if(USE_FFMPEG)
add_definitions(-DUSE_FFMPEG)
list(APPEND FEATURES FFMPEG)
pkg_search_module(LIBSWRESAMPLE QUIET libswresample)
if(NOT LIBSWRESAMPLE_FOUND)
add_definitions(-DUSE_LIBAV)
list(APPEND FEATURES LIBAV)
endif()
include_directories(AFTER ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS})
link_directories(${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS})
list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/platform/ffmpeg/ffmpeg-encoder.c")
list(APPEND FEATURE_SRC "${CMAKE_SOURCE_DIR}/src/platform/ffmpeg/ffmpeg-encoder.c")
string(REGEX MATCH "^[0-9]+" LIBAVCODEC_VERSION_MAJOR ${libavcodec_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVFORMAT_VERSION_MAJOR ${libavformat_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
@ -207,17 +215,17 @@ if(USE_FFMPEG)
endif()
if(USE_BLIP)
list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/third-party/blip_buf/blip_buf.c")
list(APPEND THIRD_PARTY_SRC "${CMAKE_SOURCE_DIR}/src/third-party/blip_buf/blip_buf.c")
add_definitions(-DRESAMPLE_LIBRARY=RESAMPLE_BLIP_BUF)
else()
add_definitions(-DRESAMPLE_LIBRARY=RESAMPLE_NN)
endif()
if(USE_MAGICK)
add_definitions(-DUSE_MAGICK)
list(APPEND FEATURES MAGICK)
include_directories(AFTER ${MAGICKWAND_INCLUDE_DIRS})
link_directories(${MAGICKWAND_LIBRARY_DIRS})
list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/platform/imagemagick/imagemagick-gif-encoder.c")
list(APPEND FEATURE_SRC "${CMAKE_SOURCE_DIR}/src/platform/imagemagick/imagemagick-gif-encoder.c")
list(APPEND DEPENDENCY_LIB ${MAGICKWAND_LIBRARIES})
string(REGEX MATCH "^[0-9]+\\.[0-9]+" MAGICKWAND_VERSION_PARTIAL ${MagickWand_VERSION})
if(${MAGICKWAND_VERSION_PARTIAL} EQUAL "6.7")
@ -229,7 +237,7 @@ if(USE_MAGICK)
endif()
if(USE_PNG)
add_definitions(-DUSE_PNG)
list(APPEND FEATURES PNG)
include_directories(AFTER ${PNG_INCLUDE_DIRS})
list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libpng12-0,zlib1g")
@ -239,7 +247,7 @@ if(USE_LIBZIP)
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
link_directories(${LIBZIP_LIBRARY_DIRS})
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
add_definitions(-DENABLE_LIBZIP)
list(APPEND FEATURES LIBZIP)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libzip2")
endif()
@ -247,7 +255,6 @@ if (USE_LZMA)
include_directories(AFTER ${CMAKE_SOURCE_DIR}/third-party/lzma)
add_definitions(-D_7ZIP_PPMD_SUPPPORT)
set(LZMA_SRC
${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-lzma.c
${CMAKE_SOURCE_DIR}/src/third-party/lzma/7zAlloc.c
${CMAKE_SOURCE_DIR}/src/third-party/lzma/7zArcIn.c
${CMAKE_SOURCE_DIR}/src/third-party/lzma/7zBuf.c
@ -265,14 +272,20 @@ if (USE_LZMA)
${CMAKE_SOURCE_DIR}/src/third-party/lzma/Ppmd7Dec.c
${CMAKE_SOURCE_DIR}/src/third-party/lzma/7zFile.c
${CMAKE_SOURCE_DIR}/src/third-party/lzma/7zStream.c)
list(APPEND UTIL_SRC ${LZMA_SRC})
add_definitions(-DENABLE_LZMA)
list(APPEND FEATURE_SRC ${LZMA_SRC})
list(APPEND FEATURES LZMA)
endif()
set(FEATURE_DEFINES)
foreach(FEATURE IN LISTS FEATURES)
list(APPEND FEATURE_DEFINES "USE_${FEATURE}")
endforeach()
# Binaries
set(SRC
set(CORE_SRC
${ARM_SRC}
${GBA_SRC}
${GBA_RR_SRC}
${GBA_SV_SRC}
${DEBUGGER_SRC}
${RENDERER_SRC}
@ -282,6 +295,10 @@ set(SRC
${OS_SRC}
${THIRD_PARTY_SRC})
set(SRC
${CORE_SRC}
${FEATURE_SRC})
if(NOT BUILD_STATIC AND NOT BUILD_SHARED)
set(BUILD_SHARED ON)
endif()
@ -290,6 +307,7 @@ if(BUILD_SHARED)
add_library(${BINARY_NAME} SHARED ${SRC})
if(BUILD_STATIC)
add_library(${BINARY_NAME}-static STATIC ${SRC})
set_target_properties(${BINARY_NAME}-static PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}")
install(TARGETS ${BINARY_NAME}-static DESTINATION lib COMPONENT lib${BINARY_NAME})
endif()
else()
@ -298,7 +316,14 @@ endif()
target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB})
install(TARGETS ${BINARY_NAME} DESTINATION lib COMPONENT lib${BINARY_NAME})
set_target_properties(${BINARY_NAME} PROPERTIES VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_ABI})
set_target_properties(${BINARY_NAME} PROPERTIES VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_ABI} COMPILE_DEFINITIONS "${FEATURE_DEFINES}")
if(BUILD_LIBRETRO)
file(GLOB RETRO_SRC ${CMAKE_SOURCE_DIR}/src/platform/libretro/*.c)
add_library(${BINARY_NAME}_libretro SHARED ${CORE_SRC} ${RETRO_SRC})
set_target_properties(${BINARY_NAME}_libretro PROPERTIES PREFIX "" COMPILE_DEFINITIONS "COLOR_16_BIT;COLOR_5_6_5")
target_link_libraries(${BINARY_NAME}_libretro m ${OS_LIB})
endif()
if(BUILD_SDL)
add_definitions(-DBUILD_SDL)
@ -355,6 +380,7 @@ message(STATUS " Better audio resampling: ${USE_BLIP}")
message(STATUS "Frontend summary:")
message(STATUS " Qt: ${BUILD_QT}")
message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}")
message(STATUS " Libretro core: ${BUILD_LIBRETRO}")
message(STATUS " Profiling: ${BUILD_PERF}")
message(STATUS "Library summary:")
message(STATUS " Static: ${BUILD_STATIC}")

View File

@ -53,6 +53,7 @@ void ARMDebuggerInit(struct ARMCore* cpu, struct ARMComponent* component) {
debugger->cpu = cpu;
debugger->state = DEBUGGER_RUNNING;
debugger->breakpoints = 0;
debugger->swBreakpoints = 0;
debugger->originalMemory = cpu->memory;
debugger->watchpoints = 0;
debugger->currentBreakpoint = 0;

View File

@ -822,11 +822,15 @@ static void _sample(struct GBAAudio* audio) {
}
produced = blip_samples_avail(audio->left);
#endif
struct GBAThread* thread = GBAThreadGetContext();
if (thread && thread->stream) {
thread->stream->postAudioFrame(thread->stream, sampleLeft, sampleRight);
if (audio->p->stream && audio->p->stream->postAudioFrame) {
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
}
bool wait = produced >= audio->samples;
GBASyncProduceAudio(audio->p->sync, wait);
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
audio->p->stream->postAudioBuffer(audio->p->stream, audio);
}
GBASyncProduceAudio(audio->p->sync, produced >= audio->samples);
}
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {

View File

@ -88,8 +88,8 @@ static void _BgAffineSet(struct GBA* gba) {
// [ sx 0 0 ] [ cos(theta) -sin(theta) 0 ] [ 1 0 cx - ox ] [ A B rx ]
// [ 0 sy 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 1 cy - oy ] = [ C D ry ]
// [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ]
ox = cpu->memory.load32(cpu, offset, 0) / 256.f;
oy = cpu->memory.load32(cpu, offset + 4, 0) / 256.f;
ox = (int32_t) cpu->memory.load32(cpu, offset, 0) / 256.f;
oy = (int32_t) cpu->memory.load32(cpu, offset + 4, 0) / 256.f;
cx = (int16_t) cpu->memory.load16(cpu, offset + 8, 0);
cy = (int16_t) cpu->memory.load16(cpu, offset + 10, 0);
sx = (int16_t) cpu->memory.load16(cpu, offset + 12, 0) / 256.f;
@ -233,6 +233,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case 0x12:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 source");
break;
}
switch (cpu->gprs[1] >> BASE_OFFSET) {
default:
@ -247,6 +248,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case 0x13:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman source");
break;
}
switch (cpu->gprs[1] >> BASE_OFFSET) {
default:
@ -262,6 +264,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case 0x15:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL source");
break;
}
switch (cpu->gprs[1] >> BASE_OFFSET) {
default:
@ -278,6 +281,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
case 0x18:
if (cpu->gprs[0] < BASE_WORKING_RAM) {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad UnFilter source");
break;
}
switch (cpu->gprs[1] >> BASE_OFFSET) {
default:

View File

@ -47,6 +47,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
struct GBA* gba = (struct GBA*) component;
gba->cpu = cpu;
gba->debugger = 0;
gba->sync = 0;
GBAInterruptHandlerInit(&cpu->irqh);
GBAMemoryInit(gba);
@ -77,7 +78,9 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
gba->romVf = 0;
gba->biosVf = 0;
gba->logHandler = 0;
gba->logLevel = GBA_LOG_INFO | GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL;
gba->stream = 0;
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
@ -106,7 +109,7 @@ void GBADestroy(struct GBA* gba) {
GBAVideoDeinit(&gba->video);
GBAAudioDeinit(&gba->audio);
GBASIODeinit(&gba->sio);
GBARRContextDestroy(gba);
gba->rr = 0;
}
void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
@ -130,7 +133,7 @@ void GBAReset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
struct GBA* gba = (struct GBA*) cpu->master;
if (!GBARRIsPlaying(gba->rr) && !GBARRIsRecording(gba->rr)) {
if (!gba->rr || (!gba->rr->isPlaying(gba->rr) && !gba->rr->isRecording(gba->rr))) {
GBASavedataUnmask(&gba->memory.savedata);
}
GBAMemoryReset(gba);
@ -547,10 +550,10 @@ static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format
threadContext->state = THREAD_CRASHED;
MutexUnlock(&threadContext->stateMutex);
}
if (threadContext->logHandler) {
threadContext->logHandler(threadContext, level, format, args);
return;
}
}
if (gba && gba->logHandler) {
gba->logHandler(threadContext, level, format, args);
return;
}
vprintf(format, args);
@ -713,10 +716,10 @@ void GBAFrameStarted(struct GBA* gba) {
void GBAFrameEnded(struct GBA* gba) {
if (gba->rr) {
GBARRNextFrame(gba->rr);
gba->rr->nextFrame(gba->rr);
}
if (gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) {
if (gba->cpu->components && gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) {
struct GBACheatDevice* device = (struct GBACheatDevice*) gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE];
size_t i;
for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
@ -733,8 +736,8 @@ void GBAFrameEnded(struct GBA* gba) {
return;
}
if (thread->stream) {
thread->stream->postVideoFrame(thread->stream, thread->renderer);
if (gba->stream) {
gba->stream->postVideoFrame(gba->stream, gba->video.renderer);
}
if (thread->frameCallback) {

View File

@ -90,9 +90,18 @@ enum {
struct GBA;
struct GBARotationSource;
struct GBAThread;
struct Patch;
struct VFile;
typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args);
struct GBAAVStream {
void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right);
void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*);
};
struct GBATimer {
uint16_t reload;
uint16_t oldReload;
@ -141,7 +150,9 @@ struct GBA {
const char* activeFile;
int logLevel;
GBALogHandler logHandler;
enum GBALogLevel logLevel;
struct GBAAVStream* stream;
enum GBAIdleLoopOptimization idleOptimization;
uint32_t idleLoop;

View File

@ -81,8 +81,8 @@ void GBAHardwareInitRTC(struct GBACartridgeHardware* hw) {
hw->rtc.bitsRead = 0;
hw->rtc.bits = 0;
hw->rtc.commandActive = 0;
hw->rtc.command.packed = 0;
hw->rtc.control.packed = 0x40;
hw->rtc.command = 0;
hw->rtc.control = 0x40;
memset(hw->rtc.time, 0, sizeof(hw->rtc.time));
}
@ -139,14 +139,14 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
}
break;
case 2:
if (!hw->p0) {
if (!(hw->pinState & 1)) {
hw->rtc.bits &= ~(1 << hw->rtc.bitsRead);
hw->rtc.bits |= hw->p1 << hw->rtc.bitsRead;
hw->rtc.bits |= ((hw->pinState & 2) >> 1) << hw->rtc.bitsRead;
} else {
if (hw->p2) {
if (hw->pinState & 4) {
// GPIO direction should always != reading
if (hw->dir1) {
if (hw->rtc.command.reading) {
if (hw->direction & 2) {
if (RTCCommandDataIsReading(hw->rtc.command)) {
GBALog(hw->p, GBA_LOG_GAME_ERROR, "Attempting to write to RTC while in read mode");
}
++hw->rtc.bitsRead;
@ -160,7 +160,7 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
--hw->rtc.bytesRemaining;
if (hw->rtc.bytesRemaining <= 0) {
hw->rtc.commandActive = 0;
hw->rtc.command.reading = 0;
hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
}
hw->rtc.bitsRead = 0;
}
@ -169,7 +169,7 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
hw->rtc.bitsRead = 0;
hw->rtc.bytesRemaining = 0;
hw->rtc.commandActive = 0;
hw->rtc.command.reading = 0;
hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
hw->rtc.transferStep = 0;
}
}
@ -180,16 +180,16 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
void _rtcProcessByte(struct GBACartridgeHardware* hw) {
--hw->rtc.bytesRemaining;
if (!hw->rtc.commandActive) {
union RTCCommandData command;
command.packed = hw->rtc.bits;
if (command.magic == 0x06) {
RTCCommandData command;
command = hw->rtc.bits;
if (RTCCommandDataGetMagic(command) == 0x06) {
hw->rtc.command = command;
hw->rtc.bytesRemaining = RTC_BYTES[hw->rtc.command.command];
hw->rtc.bytesRemaining = RTC_BYTES[RTCCommandDataGetCommand(command)];
hw->rtc.commandActive = hw->rtc.bytesRemaining > 0;
switch (command.command) {
switch (RTCCommandDataGetCommand(command)) {
case RTC_RESET:
hw->rtc.control.packed = 0;
hw->rtc.control = 0;
break;
case RTC_DATETIME:
case RTC_TIME:
@ -203,12 +203,12 @@ void _rtcProcessByte(struct GBACartridgeHardware* hw) {
GBALog(hw->p, GBA_LOG_WARN, "Invalid RTC command byte: %02X", hw->rtc.bits);
}
} else {
switch (hw->rtc.command.command) {
switch (RTCCommandDataGetCommand(hw->rtc.command)) {
case RTC_CONTROL:
hw->rtc.control.packed = hw->rtc.bits;
hw->rtc.control = hw->rtc.bits;
break;
case RTC_FORCE_IRQ:
GBALog(hw->p, GBA_LOG_STUB, "Unimplemented RTC command %u", hw->rtc.command.command);
GBALog(hw->p, GBA_LOG_STUB, "Unimplemented RTC command %u", RTCCommandDataGetCommand(hw->rtc.command));
break;
case RTC_RESET:
case RTC_DATETIME:
@ -221,15 +221,15 @@ void _rtcProcessByte(struct GBACartridgeHardware* hw) {
hw->rtc.bitsRead = 0;
if (!hw->rtc.bytesRemaining) {
hw->rtc.commandActive = 0;
hw->rtc.command.reading = 0;
hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command);
}
}
unsigned _rtcOutput(struct GBACartridgeHardware* hw) {
uint8_t outputByte = 0;
switch (hw->rtc.command.command) {
switch (RTCCommandDataGetCommand(hw->rtc.command)) {
case RTC_CONTROL:
outputByte = hw->rtc.control.packed;
outputByte = hw->rtc.control;
break;
case RTC_DATETIME:
case RTC_TIME:
@ -262,7 +262,7 @@ void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
hw->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
hw->rtc.time[2] = _rtcBCD(date.tm_mday);
hw->rtc.time[3] = _rtcBCD(date.tm_wday);
if (hw->rtc.control.hour24) {
if (RTCControlIsHour24(hw->rtc.control)) {
hw->rtc.time[4] = _rtcBCD(date.tm_hour);
} else {
hw->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
@ -292,7 +292,7 @@ void _gyroReadPins(struct GBACartridgeHardware* hw) {
return;
}
if (hw->p0) {
if (hw->pinState & 1) {
if (gyro->sample) {
gyro->sample(gyro);
}
@ -302,14 +302,14 @@ void _gyroReadPins(struct GBACartridgeHardware* hw) {
hw->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
}
if (hw->gyroEdge && !hw->p1) {
if (hw->gyroEdge && !(hw->pinState & 2)) {
// Write bit on falling edge
unsigned bit = hw->gyroSample >> 15;
hw->gyroSample <<= 1;
_outputPins(hw, bit << 2);
}
hw->gyroEdge = hw->p1;
hw->gyroEdge = !!(hw->pinState & 2);
}
// == Rumble
@ -324,7 +324,7 @@ void _rumbleReadPins(struct GBACartridgeHardware* hw) {
return;
}
rumble->setRumble(rumble, hw->p3);
rumble->setRumble(rumble, !!(hw->pinState & 8));
}
// == Light sensor
@ -337,11 +337,11 @@ void GBAHardwareInitLight(struct GBACartridgeHardware* hw) {
}
void _lightReadPins(struct GBACartridgeHardware* hw) {
if (hw->p2) {
if (hw->pinState & 4) {
// Boktai chip select
return;
}
if (hw->p1) {
if (hw->pinState & 2) {
struct GBALuminanceSource* lux = hw->p->luminanceSource;
GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Got reset");
hw->lightCounter = 0;
@ -352,10 +352,10 @@ void _lightReadPins(struct GBACartridgeHardware* hw) {
hw->lightSample = 0xFF;
}
}
if (hw->p0 && hw->lightEdge) {
if ((hw->pinState & 1) && hw->lightEdge) {
++hw->lightCounter;
}
hw->lightEdge = !hw->p0;
hw->lightEdge = !(hw->pinState & 1);
bool sendBit = hw->lightCounter >= hw->lightSample;
_outputPins(hw, sendBit << 3);
@ -424,7 +424,7 @@ uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
// == Serialization
void GBAHardwareSerialize(struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
state->hw.readWrite = hw->readWrite;
state->hw.pinState = hw->pinState;
state->hw.pinDirection = hw->direction;
@ -440,7 +440,7 @@ void GBAHardwareSerialize(struct GBACartridgeHardware* hw, struct GBASerializedS
state->hw.lightEdge = hw->lightEdge;
}
void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) {
hw->readWrite = state->hw.readWrite;
hw->pinState = state->hw.pinState;
hw->direction = state->hw.pinDirection;

View File

@ -8,6 +8,8 @@
#include "util/common.h"
#include "macros.h"
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
struct GBARotationSource {
@ -52,16 +54,10 @@ enum GPIODirection {
GPIO_READ_WRITE = 1
};
union RTCControl {
struct {
unsigned : 3;
unsigned minIRQ : 1;
unsigned : 2;
unsigned hour24 : 1;
unsigned poweroff : 1;
};
uint8_t packed;
};
DECL_BITFIELD(RTCControl, uint8_t);
DECL_BIT(RTCControl, MinIRQ, 3);
DECL_BIT(RTCControl, Hour24, 6);
DECL_BIT(RTCControl, Poweroff, 7);
enum RTCCommand {
RTC_RESET = 0,
@ -71,14 +67,10 @@ enum RTCCommand {
RTC_TIME = 6
};
union RTCCommandData {
struct {
unsigned magic : 4;
enum RTCCommand command : 3;
unsigned reading : 1;
};
uint8_t packed;
};
DECL_BITFIELD(RTCCommandData, uint8_t);
DECL_BITS(RTCCommandData, Magic, 0, 4);
DECL_BITS(RTCCommandData, Command, 4, 3);
DECL_BIT(RTCCommandData, Reading, 7);
struct GBARTC {
int bytesRemaining;
@ -86,8 +78,8 @@ struct GBARTC {
int bitsRead;
int bits;
int commandActive;
union RTCCommandData command;
union RTCControl control;
RTCCommandData command;
RTCControl control;
uint8_t time[7];
} __attribute__((packed));
@ -95,31 +87,16 @@ struct GBARumble {
void (*setRumble)(struct GBARumble*, int enable);
};
DECL_BITFIELD(GPIOPin, uint16_t);
struct GBACartridgeHardware {
struct GBA* p;
int devices;
enum GPIODirection readWrite;
uint16_t* gpioBase;
union {
struct {
unsigned p0 : 1;
unsigned p1 : 1;
unsigned p2 : 1;
unsigned p3 : 1;
};
uint16_t pinState;
};
union {
struct {
unsigned dir0 : 1;
unsigned dir1 : 1;
unsigned dir2 : 1;
unsigned dir3 : 1;
};
uint16_t direction;
};
uint16_t pinState;
uint16_t direction;
struct GBARTC rtc;
@ -149,7 +126,7 @@ void GBAHardwareTiltWrite(struct GBACartridgeHardware* gpio, uint32_t address, u
uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* gpio, uint32_t address);
struct GBASerializedState;
void GBAHardwareSerialize(struct GBACartridgeHardware* gpio, struct GBASerializedState* state);
void GBAHardwareDeserialize(struct GBACartridgeHardware* gpio, struct GBASerializedState* state);
void GBAHardwareSerialize(const struct GBACartridgeHardware* gpio, struct GBASerializedState* state);
void GBAHardwareDeserialize(struct GBACartridgeHardware* gpio, const struct GBASerializedState* state);
#endif

View File

@ -24,7 +24,7 @@ struct GBAInputMapImpl {
struct GBAAxisSave {
struct Configuration* config;
uint32_t type;
const char* sectionName;
};
struct GBAAxisEnumerate {
@ -45,6 +45,11 @@ const char* GBAKeyNames[] = {
"L"
};
static void _makeSectionName(char* sectionName, size_t len, uint32_t type) {
snprintf(sectionName, len, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
sectionName[len - 1] = '\0';
}
static bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) {
const char* strValue = ConfigurationGetValue(config, section, key);
if (!strValue) {
@ -122,11 +127,7 @@ static struct GBAInputMapImpl* _guaranteeMap(struct GBAInputMap* map, uint32_t t
return impl;
}
static void _loadKey(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey key, const char* keyName) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
sectionName[SECTION_NAME_MAX - 1] = '\0';
static void _loadKey(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey key, const char* keyName) {
char keyKey[KEY_NAME_MAX];
snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
keyKey[KEY_NAME_MAX - 1] = '\0';
@ -138,11 +139,7 @@ static void _loadKey(struct GBAInputMap* map, uint32_t type, const struct Config
GBAInputBindKey(map, type, value, key);
}
static void _loadAxis(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
sectionName[SECTION_NAME_MAX - 1] = '\0';
static void _loadAxis(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, enum GBAKey direction, const char* axisName) {
char axisKey[KEY_NAME_MAX];
snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
axisKey[KEY_NAME_MAX - 1] = '\0';
@ -179,11 +176,7 @@ static void _loadAxis(struct GBAInputMap* map, uint32_t type, const struct Confi
GBAInputBindAxis(map, type, axis, &realDescription);
}
static void _saveKey(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, enum GBAKey key, const char* keyName) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
sectionName[SECTION_NAME_MAX - 1] = '\0';
static void _saveKey(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, enum GBAKey key, const char* keyName) {
char keyKey[KEY_NAME_MAX];
snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
keyKey[KEY_NAME_MAX - 1] = '\0';
@ -195,11 +188,7 @@ static void _saveKey(const struct GBAInputMap* map, uint32_t type, struct Config
ConfigurationSetValue(config, sectionName, keyKey, keyValue);
}
static void _clearAxis(uint32_t type, struct Configuration* config, const char* axisName) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
sectionName[SECTION_NAME_MAX - 1] = '\0';
static void _clearAxis(const char* sectionName, struct Configuration* config, const char* axisName) {
char axisKey[KEY_NAME_MAX];
snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
axisKey[KEY_NAME_MAX - 1] = '\0';
@ -214,10 +203,7 @@ static void _saveAxis(uint32_t axis, void* dp, void* up) {
struct GBAAxisSave* user = up;
const struct GBAAxis* description = dp;
uint32_t type = user->type;
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type);
sectionName[SECTION_NAME_MAX - 1] = '\0';
const char* sectionName = user->sectionName;
if (description->lowDirection != GBA_KEY_NONE) {
const char* keyName = GBAKeyNames[description->lowDirection];
@ -271,6 +257,64 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) {
}
}
static void _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
_loadKey(map, type, sectionName, config, GBA_KEY_A, "A");
_loadKey(map, type, sectionName, config, GBA_KEY_B, "B");
_loadKey(map, type, sectionName, config, GBA_KEY_L, "L");
_loadKey(map, type, sectionName, config, GBA_KEY_R, "R");
_loadKey(map, type, sectionName, config, GBA_KEY_START, "Start");
_loadKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
_loadKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
_loadKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
_loadKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
_loadKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
_loadAxis(map, type, sectionName, config, GBA_KEY_A, "A");
_loadAxis(map, type, sectionName, config, GBA_KEY_B, "B");
_loadAxis(map, type, sectionName, config, GBA_KEY_L, "L");
_loadAxis(map, type, sectionName, config, GBA_KEY_R, "R");
_loadAxis(map, type, sectionName, config, GBA_KEY_START, "Start");
_loadAxis(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
_loadAxis(map, type, sectionName, config, GBA_KEY_UP, "Up");
_loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
_loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
_loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
}
static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
_saveKey(map, type, sectionName, config, GBA_KEY_A, "A");
_saveKey(map, type, sectionName, config, GBA_KEY_B, "B");
_saveKey(map, type, sectionName, config, GBA_KEY_L, "L");
_saveKey(map, type, sectionName, config, GBA_KEY_R, "R");
_saveKey(map, type, sectionName, config, GBA_KEY_START, "Start");
_saveKey(map, type, sectionName, config, GBA_KEY_SELECT, "Select");
_saveKey(map, type, sectionName, config, GBA_KEY_UP, "Up");
_saveKey(map, type, sectionName, config, GBA_KEY_DOWN, "Down");
_saveKey(map, type, sectionName, config, GBA_KEY_LEFT, "Left");
_saveKey(map, type, sectionName, config, GBA_KEY_RIGHT, "Right");
_clearAxis(sectionName, config, "A");
_clearAxis(sectionName, config, "B");
_clearAxis(sectionName, config, "L");
_clearAxis(sectionName, config, "R");
_clearAxis(sectionName, config, "Start");
_clearAxis(sectionName, config, "Select");
_clearAxis(sectionName, config, "Up");
_clearAxis(sectionName, config, "Down");
_clearAxis(sectionName, config, "Left");
_clearAxis(sectionName, config, "Right");
const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
if (!impl) {
return;
}
struct GBAAxisSave save = {
config,
sectionName
};
TableEnumerate(&impl->axes, _saveAxis, &save);
}
void GBAInputMapInit(struct GBAInputMap* map) {
map->maps = 0;
map->numMaps = 0;
@ -414,59 +458,27 @@ void GBAInputEnumerateAxes(const struct GBAInputMap* map, uint32_t type, void (h
}
void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) {
_loadKey(map, type, config, GBA_KEY_A, "A");
_loadKey(map, type, config, GBA_KEY_B, "B");
_loadKey(map, type, config, GBA_KEY_L, "L");
_loadKey(map, type, config, GBA_KEY_R, "R");
_loadKey(map, type, config, GBA_KEY_START, "Start");
_loadKey(map, type, config, GBA_KEY_SELECT, "Select");
_loadKey(map, type, config, GBA_KEY_UP, "Up");
_loadKey(map, type, config, GBA_KEY_DOWN, "Down");
_loadKey(map, type, config, GBA_KEY_LEFT, "Left");
_loadKey(map, type, config, GBA_KEY_RIGHT, "Right");
_loadAxis(map, type, config, GBA_KEY_A, "A");
_loadAxis(map, type, config, GBA_KEY_B, "B");
_loadAxis(map, type, config, GBA_KEY_L, "L");
_loadAxis(map, type, config, GBA_KEY_R, "R");
_loadAxis(map, type, config, GBA_KEY_START, "Start");
_loadAxis(map, type, config, GBA_KEY_SELECT, "Select");
_loadAxis(map, type, config, GBA_KEY_UP, "Up");
_loadAxis(map, type, config, GBA_KEY_DOWN, "Down");
_loadAxis(map, type, config, GBA_KEY_LEFT, "Left");
_loadAxis(map, type, config, GBA_KEY_RIGHT, "Right");
char sectionName[SECTION_NAME_MAX];
_makeSectionName(sectionName, SECTION_NAME_MAX, type);
_loadAll(map, type, sectionName, config);
}
void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config) {
_saveKey(map, type, config, GBA_KEY_A, "A");
_saveKey(map, type, config, GBA_KEY_B, "B");
_saveKey(map, type, config, GBA_KEY_L, "L");
_saveKey(map, type, config, GBA_KEY_R, "R");
_saveKey(map, type, config, GBA_KEY_START, "Start");
_saveKey(map, type, config, GBA_KEY_SELECT, "Select");
_saveKey(map, type, config, GBA_KEY_UP, "Up");
_saveKey(map, type, config, GBA_KEY_DOWN, "Down");
_saveKey(map, type, config, GBA_KEY_LEFT, "Left");
_saveKey(map, type, config, GBA_KEY_RIGHT, "Right");
_clearAxis(type, config, "A");
_clearAxis(type, config, "B");
_clearAxis(type, config, "L");
_clearAxis(type, config, "R");
_clearAxis(type, config, "Start");
_clearAxis(type, config, "Select");
_clearAxis(type, config, "Up");
_clearAxis(type, config, "Down");
_clearAxis(type, config, "Left");
_clearAxis(type, config, "Right");
const struct GBAInputMapImpl* impl = _lookupMapConst(map, type);
if (!impl) {
return;
}
struct GBAAxisSave save = {
config,
type
};
TableEnumerate(&impl->axes, _saveAxis, &save);
char sectionName[SECTION_NAME_MAX];
_makeSectionName(sectionName, SECTION_NAME_MAX, type);
_saveAll(map, type, sectionName, config);
}
void GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
sectionName[SECTION_NAME_MAX - 1] = '\0';
_loadAll(map, type, sectionName, config);
}
void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
char sectionName[SECTION_NAME_MAX];
snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile);
sectionName[SECTION_NAME_MAX - 1] = '\0';
_saveAll(map, type, sectionName, config);
}

View File

@ -31,7 +31,7 @@ void GBAInputMapDeinit(struct GBAInputMap*);
enum GBAKey GBAInputMapKey(const struct GBAInputMap*, uint32_t type, int key);
void GBAInputBindKey(struct GBAInputMap*, uint32_t type, int key, enum GBAKey input);
void GBAInputUnbindKey(struct GBAInputMap*, uint32_t type, int key);
void GBAInputUnbindKey(struct GBAInputMap*, uint32_t type, enum GBAKey input);
int GBAInputQueryBinding(const struct GBAInputMap*, uint32_t type, enum GBAKey input);
enum GBAKey GBAInputMapAxis(const struct GBAInputMap*, uint32_t type, int axis, int value);
@ -45,4 +45,7 @@ void GBAInputEnumerateAxes(const struct GBAInputMap*, uint32_t type, void (handl
void GBAInputMapLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*);
void GBAInputMapSave(const struct GBAInputMap*, uint32_t type, struct Configuration*);
void GBAInputProfileLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*, const char* profile);
void GBAInputProfileSave(const struct GBAInputMap*, uint32_t type, struct Configuration*, const char* profile);
#endif

View File

@ -172,7 +172,7 @@ const char* GBAIORegisterNames[] = {
"JOY_RECV_LO",
"JOY_RECV_HI",
"JOY_TRANS_LO",
"JOY_RECV_HI",
"JOY_TRANS_HI",
"JOYSTAT",
0,
0,
@ -583,12 +583,12 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
break;
case REG_KEYINPUT:
if (GBARRIsPlaying(gba->rr)) {
return 0x3FF ^ GBARRQueryInput(gba->rr);
if (gba->rr && gba->rr->isPlaying(gba->rr)) {
return 0x3FF ^ gba->rr->queryInput(gba->rr);
} else if (gba->keySource) {
uint16_t input = *gba->keySource;
if (GBARRIsRecording(gba->rr)) {
GBARRLogInput(gba->rr, input);
if (gba->rr && gba->rr->isRecording(gba->rr)) {
gba->rr->logInput(gba->rr, input);
}
return 0x3FF ^ input;
}
@ -675,7 +675,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAHardwareSerialize(&gba->memory.hw, state);
}
void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {
void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
int i;
for (i = 0; i < REG_MAX; i += 2) {
if (_isSpecialRegister[i >> 1]) {

View File

@ -161,6 +161,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address);
struct GBASerializedState;
void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state);
void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state);
void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state);
#endif

View File

@ -1461,12 +1461,12 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
cpu->cycles += cycles;
}
void GBAMemorySerialize(struct GBAMemory* memory, struct GBASerializedState* state) {
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) {
memcpy(state->wram, memory->wram, SIZE_WORKING_RAM);
memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM);
}
void GBAMemoryDeserialize(struct GBAMemory* memory, struct GBASerializedState* state) {
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state) {
memcpy(memory->wram, state->wram, SIZE_WORKING_RAM);
memcpy(memory->iwram, state->iwram, SIZE_WORKING_IRAM);
}

View File

@ -172,7 +172,7 @@ void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles);
int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles);
struct GBASerializedState;
void GBAMemorySerialize(struct GBAMemory* memory, struct GBASerializedState* state);
void GBAMemoryDeserialize(struct GBAMemory* memory, struct GBASerializedState* state);
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
#endif

View File

@ -37,6 +37,7 @@ static const int _objSizes[32] = {
static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer);
static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
@ -77,7 +78,7 @@ static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer,
void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
renderer->d.init = GBAVideoSoftwareRendererInit;
renderer->d.reset = GBAVideoSoftwareRendererInit;
renderer->d.reset = GBAVideoSoftwareRendererReset;
renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
@ -89,6 +90,21 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
}
static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
GBAVideoSoftwareRendererReset(renderer);
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
int y;
for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) {
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
int x;
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
row[x] = GBA_COLOR_WHITE;
}
}
}
static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
int i;
@ -544,7 +560,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
}
#ifdef COLOR_16_BIT
#ifdef __arm__
#ifdef __ARM_NEON
_to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
#else
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {

578
src/gba/rr/mgm.c Normal file
View File

@ -0,0 +1,578 @@
/* 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 "mgm.h"
#include "gba/gba.h"
#include "gba/serialize.h"
#include "util/vfs.h"
#define BINARY_EXT ".mgm"
#define BINARY_MAGIC "GBAb"
#define METADATA_FILENAME "metadata" BINARY_EXT
enum {
INVALID_INPUT = 0x8000
};
static void GBAMGMContextDestroy(struct GBARRContext*);
static bool GBAMGMStartPlaying(struct GBARRContext*, bool autorecord);
static void GBAMGMStopPlaying(struct GBARRContext*);
static bool GBAMGMStartRecording(struct GBARRContext*);
static void GBAMGMStopRecording(struct GBARRContext*);
static bool GBAMGMIsPlaying(const struct GBARRContext*);
static bool GBAMGMIsRecording(const struct GBARRContext*);
static void GBAMGMNextFrame(struct GBARRContext*);
static void GBAMGMLogInput(struct GBARRContext*, uint16_t input);
static uint16_t GBAMGMQueryInput(struct GBARRContext*);
static void GBAMGMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state);
static void GBAMGMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state);
static bool _loadStream(struct GBAMGMContext*, uint32_t streamId);
static bool _incrementStream(struct GBAMGMContext*, bool recursive);
static bool _finishSegment(struct GBAMGMContext*);
static bool _skipSegment(struct GBAMGMContext*);
static bool _markRerecord(struct GBAMGMContext*);
static bool _emitMagic(struct GBAMGMContext*, struct VFile* vf);
static bool _verifyMagic(struct GBAMGMContext*, struct VFile* vf);
static enum GBAMGMTag _readTag(struct GBAMGMContext*, struct VFile* vf);
static bool _seekTag(struct GBAMGMContext*, struct VFile* vf, enum GBAMGMTag tag);
static bool _emitTag(struct GBAMGMContext*, struct VFile* vf, uint8_t tag);
static bool _emitEnd(struct GBAMGMContext*, struct VFile* vf);
static bool _parseMetadata(struct GBAMGMContext*, struct VFile* vf);
static bool _markStreamNext(struct GBAMGMContext*, uint32_t newStreamId, bool recursive);
static void _streamEndReached(struct GBAMGMContext*);
static struct VFile* GBAMGMOpenSavedata(struct GBARRContext*, int flags);
static struct VFile* GBAMGMOpenSavestate(struct GBARRContext*, int flags);
void GBAMGMContextCreate(struct GBAMGMContext* mgm) {
memset(mgm, 0, sizeof(*mgm));
mgm->d.destroy = GBAMGMContextDestroy;
mgm->d.startPlaying = GBAMGMStartPlaying;
mgm->d.stopPlaying = GBAMGMStopPlaying;
mgm->d.startRecording = GBAMGMStartRecording;
mgm->d.stopRecording = GBAMGMStopRecording;
mgm->d.isPlaying = GBAMGMIsPlaying;
mgm->d.isRecording = GBAMGMIsRecording;
mgm->d.nextFrame = GBAMGMNextFrame;
mgm->d.logInput = GBAMGMLogInput;
mgm->d.queryInput = GBAMGMQueryInput;
mgm->d.stateSaved = GBAMGMStateSaved;
mgm->d.stateLoaded = GBAMGMStateLoaded;
mgm->d.openSavedata = GBAMGMOpenSavedata;
mgm->d.openSavestate = GBAMGMOpenSavestate;
}
void GBAMGMContextDestroy(struct GBARRContext* rr) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (mgm->metadataFile) {
mgm->metadataFile->close(mgm->metadataFile);
}
}
bool GBAMGMSetStream(struct GBAMGMContext* mgm, struct VDir* stream) {
if (mgm->movieStream && !mgm->movieStream->close(mgm->movieStream)) {
return false;
}
if (mgm->metadataFile && !mgm->metadataFile->close(mgm->metadataFile)) {
return false;
}
mgm->streamDir = stream;
mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR);
mgm->currentInput = INVALID_INPUT;
if (!_parseMetadata(mgm, mgm->metadataFile)) {
mgm->metadataFile->close(mgm->metadataFile);
mgm->metadataFile = 0;
mgm->maxStreamId = 0;
}
mgm->streamId = 1;
mgm->movieStream = 0;
return true;
}
bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom) {
if (mgm->metadataFile) {
mgm->metadataFile->truncate(mgm->metadataFile, 0);
} else {
mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR);
}
_emitMagic(mgm, mgm->metadataFile);
mgm->d.initFrom = initFrom;
mgm->initFromOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
_emitTag(mgm, mgm->metadataFile, TAG_INIT | initFrom);
mgm->streamId = 0;
mgm->maxStreamId = 0;
_emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM);
mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
mgm->d.rrCount = 0;
_emitTag(mgm, mgm->metadataFile, TAG_RR_COUNT);
mgm->rrCountOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount));
return true;
}
bool _loadStream(struct GBAMGMContext* mgm, uint32_t streamId) {
if (mgm->movieStream && !mgm->movieStream->close(mgm->movieStream)) {
return false;
}
mgm->movieStream = 0;
mgm->streamId = streamId;
mgm->currentInput = INVALID_INPUT;
char buffer[14];
snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId);
if (mgm->d.isRecording(&mgm->d)) {
int flags = O_CREAT | O_RDWR;
if (streamId > mgm->maxStreamId) {
flags |= O_TRUNC;
}
mgm->movieStream = mgm->streamDir->openFile(mgm->streamDir, buffer, flags);
} else if (mgm->d.isPlaying(&mgm->d)) {
mgm->movieStream = mgm->streamDir->openFile(mgm->streamDir, buffer, O_RDONLY);
mgm->peekedTag = TAG_INVALID;
if (!mgm->movieStream || !_verifyMagic(mgm, mgm->movieStream) || !_seekTag(mgm, mgm->movieStream, TAG_BEGIN)) {
mgm->d.stopPlaying(&mgm->d);
}
}
GBALog(0, GBA_LOG_DEBUG, "[RR] Loading segment: %u", streamId);
mgm->d.frames = 0;
mgm->d.lagFrames = 0;
return true;
}
bool _incrementStream(struct GBAMGMContext* mgm, bool recursive) {
uint32_t newStreamId = mgm->maxStreamId + 1;
uint32_t oldStreamId = mgm->streamId;
if (mgm->d.isRecording(&mgm->d) && mgm->movieStream) {
if (!_markStreamNext(mgm, newStreamId, recursive)) {
return false;
}
}
if (!_loadStream(mgm, newStreamId)) {
return false;
}
GBALog(0, GBA_LOG_DEBUG, "[RR] New segment: %u", newStreamId);
_emitMagic(mgm, mgm->movieStream);
mgm->maxStreamId = newStreamId;
_emitTag(mgm, mgm->movieStream, TAG_PREVIOUSLY);
mgm->movieStream->write(mgm->movieStream, &oldStreamId, sizeof(oldStreamId));
_emitTag(mgm, mgm->movieStream, TAG_BEGIN);
mgm->metadataFile->seek(mgm->metadataFile, mgm->maxStreamIdOffset, SEEK_SET);
mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
mgm->previously = oldStreamId;
return true;
}
bool GBAMGMStartPlaying(struct GBARRContext* rr, bool autorecord) {
if (rr->isRecording(rr) || rr->isPlaying(rr)) {
return false;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
mgm->isPlaying = true;
if (!_loadStream(mgm, 1)) {
mgm->isPlaying = false;
return false;
}
mgm->autorecord = autorecord;
return true;
}
void GBAMGMStopPlaying(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
mgm->isPlaying = false;
if (mgm->movieStream) {
mgm->movieStream->close(mgm->movieStream);
mgm->movieStream = 0;
}
}
bool GBAMGMStartRecording(struct GBARRContext* rr) {
if (rr->isRecording(rr) || rr->isPlaying(rr)) {
return false;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (!mgm->maxStreamIdOffset) {
_emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM);
mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
}
mgm->isRecording = true;
return _incrementStream(mgm, false);
}
void GBAMGMStopRecording(struct GBARRContext* rr) {
if (!rr->isRecording(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
mgm->isRecording = false;
if (mgm->movieStream) {
_emitEnd(mgm, mgm->movieStream);
mgm->movieStream->close(mgm->movieStream);
mgm->movieStream = 0;
}
}
bool GBAMGMIsPlaying(const struct GBARRContext* rr) {
const struct GBAMGMContext* mgm = (const struct GBAMGMContext*) rr;
return mgm->isPlaying;
}
bool GBAMGMIsRecording(const struct GBARRContext* rr) {
const struct GBAMGMContext* mgm = (const struct GBAMGMContext*) rr;
return mgm->isRecording;
}
void GBAMGMNextFrame(struct GBARRContext* rr) {
if (!rr->isRecording(rr) && !rr->isPlaying(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (rr->isPlaying(rr)) {
while (mgm->peekedTag == TAG_INPUT) {
_readTag(mgm, mgm->movieStream);
GBALog(0, GBA_LOG_WARN, "[RR] Desync detected!");
}
if (mgm->peekedTag == TAG_LAG) {
GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame marked in stream");
if (mgm->inputThisFrame) {
GBALog(0, GBA_LOG_WARN, "[RR] Lag frame in stream does not match movie");
}
}
}
++mgm->d.frames;
GBALog(0, GBA_LOG_DEBUG, "[RR] Frame: %u", mgm->d.frames);
if (!mgm->inputThisFrame) {
++mgm->d.lagFrames;
GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame: %u", mgm->d.lagFrames);
}
if (rr->isRecording(rr)) {
if (!mgm->inputThisFrame) {
_emitTag(mgm, mgm->movieStream, TAG_LAG);
}
_emitTag(mgm, mgm->movieStream, TAG_FRAME);
mgm->inputThisFrame = false;
} else {
if (!_seekTag(mgm, mgm->movieStream, TAG_FRAME)) {
_streamEndReached(mgm);
}
}
}
void GBAMGMLogInput(struct GBARRContext* rr, uint16_t keys) {
if (!rr->isRecording(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (keys != mgm->currentInput) {
_emitTag(mgm, mgm->movieStream, TAG_INPUT);
mgm->movieStream->write(mgm->movieStream, &keys, sizeof(keys));
mgm->currentInput = keys;
}
GBALog(0, GBA_LOG_DEBUG, "[RR] Input log: %03X", mgm->currentInput);
mgm->inputThisFrame = true;
}
uint16_t GBAMGMQueryInput(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return 0;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (mgm->peekedTag == TAG_INPUT) {
_readTag(mgm, mgm->movieStream);
}
mgm->inputThisFrame = true;
if (mgm->currentInput == INVALID_INPUT) {
GBALog(0, GBA_LOG_WARN, "[RR] Stream did not specify input");
}
GBALog(0, GBA_LOG_DEBUG, "[RR] Input replay: %03X", mgm->currentInput);
return mgm->currentInput;
}
void GBAMGMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (rr->isRecording(rr)) {
state->associatedStreamId = mgm->streamId;
_finishSegment(mgm);
}
}
void GBAMGMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (rr->isRecording(rr)) {
if (state->associatedStreamId != mgm->streamId) {
_loadStream(mgm, state->associatedStreamId);
_incrementStream(mgm, true);
} else {
_finishSegment(mgm);
}
_markRerecord(mgm);
} else if (rr->isPlaying(rr)) {
_loadStream(mgm, state->associatedStreamId);
_skipSegment(mgm);
}
}
bool _finishSegment(struct GBAMGMContext* mgm) {
if (mgm->movieStream) {
if (!_emitEnd(mgm, mgm->movieStream)) {
return false;
}
}
return _incrementStream(mgm, false);
}
bool _skipSegment(struct GBAMGMContext* mgm) {
mgm->nextTime = 0;
while (_readTag(mgm, mgm->movieStream) != TAG_EOF);
if (!mgm->nextTime || !_loadStream(mgm, mgm->nextTime)) {
_streamEndReached(mgm);
return false;
}
return true;
}
bool _markRerecord(struct GBAMGMContext* mgm) {
++mgm->d.rrCount;
mgm->metadataFile->seek(mgm->metadataFile, mgm->rrCountOffset, SEEK_SET);
mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount));
return true;
}
bool _emitMagic(struct GBAMGMContext* mgm, struct VFile* vf) {
UNUSED(mgm);
return vf->write(vf, BINARY_MAGIC, 4) == 4;
}
bool _verifyMagic(struct GBAMGMContext* mgm, struct VFile* vf) {
UNUSED(mgm);
char buffer[4];
if (vf->read(vf, buffer, sizeof(buffer)) != sizeof(buffer)) {
return false;
}
if (memcmp(buffer, BINARY_MAGIC, sizeof(buffer)) != 0) {
return false;
}
return true;
}
enum GBAMGMTag _readTag(struct GBAMGMContext* mgm, struct VFile* vf) {
if (!mgm || !vf) {
return TAG_EOF;
}
enum GBAMGMTag tag = mgm->peekedTag;
switch (tag) {
case TAG_INPUT:
vf->read(vf, &mgm->currentInput, sizeof(uint16_t));
break;
case TAG_PREVIOUSLY:
vf->read(vf, &mgm->previously, sizeof(mgm->previously));
break;
case TAG_NEXT_TIME:
vf->read(vf, &mgm->nextTime, sizeof(mgm->nextTime));
break;
case TAG_MAX_STREAM:
vf->read(vf, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
break;
case TAG_FRAME_COUNT:
vf->read(vf, &mgm->d.frames, sizeof(mgm->d.frames));
break;
case TAG_LAG_COUNT:
vf->read(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames));
break;
case TAG_RR_COUNT:
vf->read(vf, &mgm->d.rrCount, sizeof(mgm->d.rrCount));
break;
case TAG_INIT_EX_NIHILO:
mgm->d.initFrom = INIT_EX_NIHILO;
break;
case TAG_INIT_FROM_SAVEGAME:
mgm->d.initFrom = INIT_FROM_SAVEGAME;
break;
case TAG_INIT_FROM_SAVESTATE:
mgm->d.initFrom = INIT_FROM_SAVESTATE;
break;
case TAG_INIT_FROM_BOTH:
mgm->d.initFrom = INIT_FROM_BOTH;
break;
// To be spec'd
case TAG_AUTHOR:
case TAG_COMMENT:
break;
// Empty markers
case TAG_FRAME:
case TAG_LAG:
case TAG_BEGIN:
case TAG_END:
case TAG_INVALID:
case TAG_EOF:
break;
}
uint8_t tagBuffer;
if (vf->read(vf, &tagBuffer, 1) != 1) {
mgm->peekedTag = TAG_EOF;
} else {
mgm->peekedTag = tagBuffer;
}
if (mgm->peekedTag == TAG_END) {
_skipSegment(mgm);
}
return tag;
}
bool _seekTag(struct GBAMGMContext* mgm, struct VFile* vf, enum GBAMGMTag tag) {
enum GBAMGMTag readTag;
while ((readTag = _readTag(mgm, vf)) != tag) {
if (readTag == TAG_EOF) {
return false;
}
}
return true;
}
bool _emitTag(struct GBAMGMContext* mgm, struct VFile* vf, uint8_t tag) {
UNUSED(mgm);
return vf->write(vf, &tag, sizeof(tag)) == sizeof(tag);
}
bool _parseMetadata(struct GBAMGMContext* mgm, struct VFile* vf) {
if (!_verifyMagic(mgm, vf)) {
return false;
}
while (_readTag(mgm, vf) != TAG_EOF) {
switch (mgm->peekedTag) {
case TAG_MAX_STREAM:
mgm->maxStreamIdOffset = vf->seek(vf, 0, SEEK_CUR);
break;
case TAG_INIT_EX_NIHILO:
case TAG_INIT_FROM_SAVEGAME:
case TAG_INIT_FROM_SAVESTATE:
case TAG_INIT_FROM_BOTH:
mgm->initFromOffset = vf->seek(vf, 0, SEEK_CUR);
break;
case TAG_RR_COUNT:
mgm->rrCountOffset = vf->seek(vf, 0, SEEK_CUR);
break;
default:
break;
}
}
return true;
}
bool _emitEnd(struct GBAMGMContext* mgm, struct VFile* vf) {
// TODO: Error check
_emitTag(mgm, vf, TAG_END);
_emitTag(mgm, vf, TAG_FRAME_COUNT);
vf->write(vf, &mgm->d.frames, sizeof(mgm->d.frames));
_emitTag(mgm, vf, TAG_LAG_COUNT);
vf->write(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames));
_emitTag(mgm, vf, TAG_NEXT_TIME);
uint32_t newStreamId = 0;
vf->write(vf, &newStreamId, sizeof(newStreamId));
return true;
}
bool _markStreamNext(struct GBAMGMContext* mgm, uint32_t newStreamId, bool recursive) {
if (mgm->movieStream->seek(mgm->movieStream, -sizeof(newStreamId) - 1, SEEK_END) < 0) {
return false;
}
uint8_t tagBuffer;
if (mgm->movieStream->read(mgm->movieStream, &tagBuffer, 1) != 1) {
return false;
}
if (tagBuffer != TAG_NEXT_TIME) {
return false;
}
if (mgm->movieStream->write(mgm->movieStream, &newStreamId, sizeof(newStreamId)) != sizeof(newStreamId)) {
return false;
}
if (recursive) {
if (mgm->movieStream->seek(mgm->movieStream, 0, SEEK_SET) < 0) {
return false;
}
if (!_verifyMagic(mgm, mgm->movieStream)) {
return false;
}
_readTag(mgm, mgm->movieStream);
if (_readTag(mgm, mgm->movieStream) != TAG_PREVIOUSLY) {
return false;
}
if (mgm->previously == 0) {
return true;
}
uint32_t currentStreamId = mgm->streamId;
if (!_loadStream(mgm, mgm->previously)) {
return false;
}
return _markStreamNext(mgm, currentStreamId, mgm->previously);
}
return true;
}
void _streamEndReached(struct GBAMGMContext* mgm) {
if (!mgm->d.isPlaying(&mgm->d)) {
return;
}
uint32_t endStreamId = mgm->streamId;
mgm->d.stopPlaying(&mgm->d);
if (mgm->autorecord) {
mgm->isRecording = true;
_loadStream(mgm, endStreamId);
_incrementStream(mgm, false);
}
}
struct VFile* GBAMGMOpenSavedata(struct GBARRContext* rr, int flags) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
return mgm->streamDir->openFile(mgm->streamDir, "movie.sav", flags);
}
struct VFile* GBAMGMOpenSavestate(struct GBARRContext* rr, int flags) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
return mgm->streamDir->openFile(mgm->streamDir, "movie.ssm", flags);
}

82
src/gba/rr/mgm.h Normal file
View File

@ -0,0 +1,82 @@
/* 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 RR_MGM_H
#define RR_MGM_H
#include "util/common.h"
#include "gba/supervisor/rr.h"
struct GBA;
struct VDir;
struct VFile;
enum GBAMGMTag {
// Playback tags
TAG_INVALID = 0x00,
TAG_INPUT = 0x01,
TAG_FRAME = 0x02,
TAG_LAG = 0x03,
// Stream chunking tags
TAG_BEGIN = 0x10,
TAG_END = 0x11,
TAG_PREVIOUSLY = 0x12,
TAG_NEXT_TIME = 0x13,
TAG_MAX_STREAM = 0x14,
// Recording information tags
TAG_FRAME_COUNT = 0x20,
TAG_LAG_COUNT = 0x21,
TAG_RR_COUNT = 0x22,
TAG_INIT = 0x24,
TAG_INIT_EX_NIHILO = 0x24 | INIT_EX_NIHILO,
TAG_INIT_FROM_SAVEGAME = 0x24 | INIT_FROM_SAVEGAME,
TAG_INIT_FROM_SAVESTATE = 0x24 | INIT_FROM_SAVESTATE,
TAG_INIT_FROM_BOTH = 0x24 | INIT_FROM_BOTH,
// User metadata tags
TAG_AUTHOR = 0x30,
TAG_COMMENT = 0x31,
TAG_EOF = INT_MAX
};
struct GBAMGMContext {
struct GBARRContext d;
// Playback state
bool isPlaying;
bool autorecord;
// Recording state
bool isRecording;
bool inputThisFrame;
// Metadata
uint32_t streamId;
uint32_t maxStreamId;
off_t maxStreamIdOffset;
off_t initFromOffset;
off_t rrCountOffset;
// Streaming state
struct VDir* streamDir;
struct VFile* metadataFile;
struct VFile* movieStream;
uint16_t currentInput;
enum GBAMGMTag peekedTag;
uint32_t nextTime;
uint32_t previously;
};
void GBAMGMContextCreate(struct GBAMGMContext*);
bool GBAMGMSetStream(struct GBAMGMContext* mgm, struct VDir* stream);
bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom);
#endif

183
src/gba/rr/vbm.c Normal file
View File

@ -0,0 +1,183 @@
/* 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 "vbm.h"
#include "gba/gba.h"
#include "gba/serialize.h"
#include "util/vfs.h"
static const char VBM_MAGIC[] = "VBM\x1A";
static void GBAVBMContextDestroy(struct GBARRContext*);
static bool GBAVBMStartPlaying(struct GBARRContext*, bool autorecord);
static void GBAVBMStopPlaying(struct GBARRContext*);
static bool GBAVBMStartRecording(struct GBARRContext*);
static void GBAVBMStopRecording(struct GBARRContext*);
static bool GBAVBMIsPlaying(const struct GBARRContext*);
static bool GBAVBMIsRecording(const struct GBARRContext*);
static void GBAVBMNextFrame(struct GBARRContext*);
static uint16_t GBAVBMQueryInput(struct GBARRContext*);
static void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state);
static void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state);
static struct VFile* GBAVBMOpenSavedata(struct GBARRContext*, int flags);
static struct VFile* GBAVBMOpenSavestate(struct GBARRContext*, int flags);
void GBAVBMContextCreate(struct GBAVBMContext* vbm) {
memset(vbm, 0, sizeof(*vbm));
vbm->d.destroy = GBAVBMContextDestroy;
vbm->d.startPlaying = GBAVBMStartPlaying;
vbm->d.stopPlaying = GBAVBMStopPlaying;
vbm->d.startRecording = GBAVBMStartRecording;
vbm->d.stopRecording = GBAVBMStopRecording;
vbm->d.isPlaying = GBAVBMIsPlaying;
vbm->d.isRecording = GBAVBMIsRecording;
vbm->d.nextFrame = GBAVBMNextFrame;
vbm->d.logInput = 0;
vbm->d.queryInput = GBAVBMQueryInput;
vbm->d.stateSaved = GBAVBMStateSaved;
vbm->d.stateLoaded = GBAVBMStateLoaded;
vbm->d.openSavedata = GBAVBMOpenSavedata;
vbm->d.openSavestate = GBAVBMOpenSavestate;
}
bool GBAVBMStartPlaying(struct GBARRContext* rr, bool autorecord) {
if (rr->isRecording(rr) || rr->isPlaying(rr) || autorecord) {
return false;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
vbm->isPlaying = true;
vbm->vbmFile->seek(vbm->vbmFile, vbm->inputOffset, SEEK_SET);
return true;
}
void GBAVBMStopPlaying(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
vbm->isPlaying = false;
}
bool GBAVBMStartRecording(struct GBARRContext* rr) {
UNUSED(rr);
return false;
}
void GBAVBMStopRecording(struct GBARRContext* rr) {
UNUSED(rr);
}
bool GBAVBMIsPlaying(const struct GBARRContext* rr) {
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
return vbm->isPlaying;
}
bool GBAVBMIsRecording(const struct GBARRContext* rr) {
UNUSED(rr);
return false;
}
void GBAVBMNextFrame(struct GBARRContext* rr) {
UNUSED(rr);
}
uint16_t GBAVBMQueryInput(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return 0;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
uint16_t input;
vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input));
return input & 0x3FF;
}
void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) {
UNUSED(rr);
UNUSED(state);
}
void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) {
UNUSED(rr);
UNUSED(state);
}
struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) {
UNUSED(rr);
UNUSED(flags);
return 0;
}
struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) {
UNUSED(rr);
UNUSED(flags);
return 0;
}
void GBAVBMContextDestroy(struct GBARRContext* rr) {
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
if (vbm->vbmFile) {
vbm->vbmFile->close(vbm->vbmFile);
}
}
bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) {
vf->seek(vf, 0, SEEK_SET);
char magic[4];
vf->read(vf, magic, sizeof(magic));
if (memcmp(magic, VBM_MAGIC, sizeof(magic)) != 0) {
return false;
}
uint32_t id;
vf->read(vf, &id, sizeof(id));
if (id != 1) {
return false;
}
vf->seek(vf, 4, SEEK_CUR);
vf->read(vf, &vbm->d.frames, sizeof(vbm->d.frames));
vf->read(vf, &vbm->d.rrCount, sizeof(vbm->d.rrCount));
uint8_t flags;
vf->read(vf, &flags, sizeof(flags));
if (flags & 1) {
// Incompatible savestate format
return false;
}
if (flags & 2) {
// TODO: Implement SRAM loading
return false;
}
vf->seek(vf, 1, SEEK_CUR);
vf->read(vf, &flags, sizeof(flags));
if ((flags & 0x7) != 1) {
// Non-GBA movie
return false;
}
// TODO: parse more flags
vf->seek(vf, 0x3C, SEEK_SET);
vf->read(vf, &vbm->inputOffset, sizeof(vbm->inputOffset));
vf->seek(vf, vbm->inputOffset, SEEK_SET);
vbm->vbmFile = vf;
return true;
}

21
src/gba/rr/vbm.h Normal file
View File

@ -0,0 +1,21 @@
/* 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 "util/common.h"
#include "gba/supervisor/rr.h"
struct GBAVBMContext {
struct GBARRContext d;
bool isPlaying;
struct VFile* vbmFile;
int32_t inputOffset;
};
void GBAVBMContextCreate(struct GBAVBMContext*);
bool GBAVBMSetStream(struct GBAVBMContext*, struct VFile*);

View File

@ -49,15 +49,13 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAVideoSerialize(&gba->video, state);
GBAAudioSerialize(&gba->audio, state);
if (GBARRIsRecording(gba->rr)) {
state->associatedStreamId = gba->rr->streamId;
GBARRFinishSegment(gba->rr);
} else {
state->associatedStreamId = 0;
state->associatedStreamId = 0;
if (gba->rr) {
gba->rr->stateSaved(gba->rr, state);
}
}
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
void GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (state->versionMagic != GBA_SAVESTATE_MAGIC) {
GBALog(gba, GBA_LOG_WARN, "Invalid or too new savestate");
return;
@ -114,17 +112,8 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
GBAVideoDeserialize(&gba->video, state);
GBAAudioDeserialize(&gba->audio, state);
if (GBARRIsRecording(gba->rr)) {
if (state->associatedStreamId != gba->rr->streamId) {
GBARRLoadStream(gba->rr, state->associatedStreamId);
GBARRIncrementStream(gba->rr, true);
} else {
GBARRFinishSegment(gba->rr);
}
GBARRMarkRerecord(gba->rr);
} else if (GBARRIsPlaying(gba->rr)) {
GBARRLoadStream(gba->rr, state->associatedStreamId);
GBARRSkipSegment(gba->rr);
if (gba->rr) {
gba->rr->stateLoaded(gba->rr, state);
}
}

View File

@ -292,7 +292,7 @@ struct VDir;
struct GBAThread;
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state);
void GBADeserialize(struct GBA* gba, const struct GBASerializedState* state);
bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, bool screenshot);
bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot);

View File

@ -36,11 +36,11 @@ static void _switchMode(struct GBASIO* sio) {
} else {
sio->mode = (enum GBASIOMode) (mode & 0xC);
}
if (oldMode != mode) {
if (oldMode != sio->mode) {
if (sio->activeDriver && sio->activeDriver->unload) {
sio->activeDriver->unload(sio->activeDriver);
}
sio->activeDriver = _lookupDriver(sio, mode);
sio->activeDriver = _lookupDriver(sio, sio->mode);
if (sio->activeDriver && sio->activeDriver->load) {
sio->activeDriver->load(sio->activeDriver);
}

View File

@ -21,6 +21,13 @@ static const struct GBACartridgeOverride _overrides[] = {
{ "U32E", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
{ "U32P", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
// Dragon Ball Z - The Legacy of Goku
{ "ALGP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE },
// Dragon Ball Z - Taiketsu
{ "BDBE", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE },
{ "BDBP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE },
// Drill Dozer
{ "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE },
{ "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE },
@ -28,6 +35,9 @@ static const struct GBACartridgeOverride _overrides[] = {
// Final Fantasy Tactics Advance
{ "AFXE", SAVEDATA_FLASH512, HW_NONE, 0x8000428 },
// F-Zero - Climax
{ "BFTJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
// Golden Sun: The Lost Age
{ "AGFE", SAVEDATA_FLASH512, HW_NONE, 0x801353A },
@ -77,15 +87,30 @@ static const struct GBACartridgeOverride _overrides[] = {
{ "BPRJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPRE", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPRP", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPRI", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPRS", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPRD", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPRF", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
// Pokemon LeafGreen
{ "BPGJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPGE", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPGP", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPGI", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPGS", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPGD", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
{ "BPGF", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
// RockMan EXE 4.5 - Real Operation
{ "BR4J", SAVEDATA_FLASH512, HW_RTC, IDLE_LOOP_NONE },
// Rocky
{ "AR8E", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE },
{ "AROP", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE },
// Sennen Kazoku
{ "BKAJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
// Shin Bokura no Taiyou: Gyakushuu no Sabata
{ "U33J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },

View File

@ -5,64 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "rr.h"
#include "gba/gba.h"
#include "gba/serialize.h"
#include "util/vfs.h"
#define BINARY_EXT ".dat"
#define BINARY_MAGIC "GBAb"
#define METADATA_FILENAME "metadata" BINARY_EXT
enum {
INVALID_INPUT = 0x8000
};
static bool _emitMagic(struct GBARRContext* rr, struct VFile* vf);
static bool _verifyMagic(struct GBARRContext* rr, struct VFile* vf);
static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf);
static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag);
static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag);
static bool _emitEnd(struct GBARRContext* rr, struct VFile* vf);
static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf);
static bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursive);
static void _streamEndReached(struct GBARRContext* rr);
static struct VFile* _openSavedata(struct GBARRContext* rr, int flags);
static struct VFile* _openSavestate(struct GBARRContext* rr, int flags);
void GBARRContextCreate(struct GBA* gba) {
if (gba->rr) {
return;
}
gba->rr = calloc(1, sizeof(*gba->rr));
}
void GBARRContextDestroy(struct GBA* gba) {
if (!gba->rr) {
return;
}
if (GBARRIsPlaying(gba->rr)) {
GBARRStopPlaying(gba->rr);
}
if (GBARRIsRecording(gba->rr)) {
GBARRStopRecording(gba->rr);
}
if (gba->rr->metadataFile) {
gba->rr->metadataFile->close(gba->rr->metadataFile);
}
if (gba->rr->savedata) {
gba->rr->savedata->close(gba->rr->savedata);
}
free(gba->rr);
gba->rr = 0;
}
void GBARRSaveState(struct GBA* gba) {
void GBARRInitRecord(struct GBA* gba) {
if (!gba || !gba->rr) {
return;
}
@ -71,17 +16,17 @@ void GBARRSaveState(struct GBA* gba) {
if (gba->rr->savedata) {
gba->rr->savedata->close(gba->rr->savedata);
}
gba->rr->savedata = _openSavedata(gba->rr, O_TRUNC | O_CREAT | O_WRONLY);
gba->rr->savedata = gba->rr->openSavedata(gba->rr, O_TRUNC | O_CREAT | O_WRONLY);
GBASavedataClone(&gba->memory.savedata, gba->rr->savedata);
gba->rr->savedata->close(gba->rr->savedata);
gba->rr->savedata = _openSavedata(gba->rr, O_RDONLY);
gba->rr->savedata = gba->rr->openSavedata(gba->rr, O_RDONLY);
GBASavedataMask(&gba->memory.savedata, gba->rr->savedata);
} else {
GBASavedataMask(&gba->memory.savedata, 0);
}
if (gba->rr->initFrom & INIT_FROM_SAVESTATE) {
struct VFile* vf = _openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR);
struct VFile* vf = gba->rr->openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR);
GBASaveStateNamed(gba, vf, false);
vf->close(vf);
} else {
@ -89,7 +34,7 @@ void GBARRSaveState(struct GBA* gba) {
}
}
void GBARRLoadState(struct GBA* gba) {
void GBARRInitPlay(struct GBA* gba) {
if (!gba || !gba->rr) {
return;
}
@ -98,14 +43,14 @@ void GBARRLoadState(struct GBA* gba) {
if (gba->rr->savedata) {
gba->rr->savedata->close(gba->rr->savedata);
}
gba->rr->savedata = _openSavedata(gba->rr, O_RDONLY);
gba->rr->savedata = gba->rr->openSavedata(gba->rr, O_RDONLY);
GBASavedataMask(&gba->memory.savedata, gba->rr->savedata);
} else {
GBASavedataMask(&gba->memory.savedata, 0);
}
if (gba->rr->initFrom & INIT_FROM_SAVESTATE) {
struct VFile* vf = _openSavestate(gba->rr, O_RDONLY);
struct VFile* vf = gba->rr->openSavestate(gba->rr, O_RDONLY);
GBALoadStateNamed(gba, vf);
vf->close(vf);
} else {
@ -113,460 +58,16 @@ void GBARRLoadState(struct GBA* gba) {
}
}
bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) {
if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) {
return false;
void GBARRDestroy(struct GBARRContext* rr) {
if (rr->isPlaying(rr)) {
rr->stopPlaying(rr);
}
if (rr->metadataFile && !rr->metadataFile->close(rr->metadataFile)) {
return false;
if (rr->isRecording(rr)) {
rr->stopRecording(rr);
}
rr->streamDir = stream;
rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR);
rr->currentInput = INVALID_INPUT;
if (!_parseMetadata(rr, rr->metadataFile)) {
rr->metadataFile->close(rr->metadataFile);
rr->metadataFile = 0;
rr->maxStreamId = 0;
if (rr->savedata) {
rr->savedata->close(rr->savedata);
rr->savedata = 0;
}
rr->streamId = 1;
rr->movieStream = 0;
return true;
}
bool GBARRReinitStream(struct GBARRContext* rr, enum GBARRInitFrom initFrom) {
if (!rr) {
return false;
}
if (rr->metadataFile) {
rr->metadataFile->truncate(rr->metadataFile, 0);
} else {
rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR);
}
_emitMagic(rr, rr->metadataFile);
rr->initFrom = initFrom;
rr->initFromOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR);
_emitTag(rr, rr->metadataFile, TAG_INIT | initFrom);
rr->streamId = 0;
rr->maxStreamId = 0;
_emitTag(rr, rr->metadataFile, TAG_MAX_STREAM);
rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR);
rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId));
rr->rrCount = 0;
_emitTag(rr, rr->metadataFile, TAG_RR_COUNT);
rr->rrCountOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR);
rr->metadataFile->write(rr->metadataFile, &rr->rrCount, sizeof(rr->rrCount));
return true;
}
bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) {
if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) {
return false;
}
rr->movieStream = 0;
rr->streamId = streamId;
rr->currentInput = INVALID_INPUT;
char buffer[14];
snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId);
if (GBARRIsRecording(rr)) {
int flags = O_CREAT | O_RDWR;
if (streamId > rr->maxStreamId) {
flags |= O_TRUNC;
}
rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, flags);
} else if (GBARRIsPlaying(rr)) {
rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY);
rr->peekedTag = TAG_INVALID;
if (!rr->movieStream || !_verifyMagic(rr, rr->movieStream) || !_seekTag(rr, rr->movieStream, TAG_BEGIN)) {
GBARRStopPlaying(rr);
}
}
GBALog(0, GBA_LOG_DEBUG, "[RR] Loading segment: %u", streamId);
rr->frames = 0;
rr->lagFrames = 0;
return true;
}
bool GBARRIncrementStream(struct GBARRContext* rr, bool recursive) {
uint32_t newStreamId = rr->maxStreamId + 1;
uint32_t oldStreamId = rr->streamId;
if (GBARRIsRecording(rr) && rr->movieStream) {
if (!_markStreamNext(rr, newStreamId, recursive)) {
return false;
}
}
if (!GBARRLoadStream(rr, newStreamId)) {
return false;
}
GBALog(0, GBA_LOG_DEBUG, "[RR] New segment: %u", newStreamId);
_emitMagic(rr, rr->movieStream);
rr->maxStreamId = newStreamId;
_emitTag(rr, rr->movieStream, TAG_PREVIOUSLY);
rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId));
_emitTag(rr, rr->movieStream, TAG_BEGIN);
rr->metadataFile->seek(rr->metadataFile, rr->maxStreamIdOffset, SEEK_SET);
rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId));
rr->previously = oldStreamId;
return true;
}
bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) {
if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
return false;
}
rr->isPlaying = true;
if (!GBARRLoadStream(rr, 1)) {
rr->isPlaying = false;
return false;
}
rr->autorecord = autorecord;
return true;
}
void GBARRStopPlaying(struct GBARRContext* rr) {
if (!GBARRIsPlaying(rr)) {
return;
}
rr->isPlaying = false;
if (rr->movieStream) {
rr->movieStream->close(rr->movieStream);
rr->movieStream = 0;
}
}
bool GBARRStartRecording(struct GBARRContext* rr) {
if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) {
return false;
}
if (!rr->maxStreamIdOffset) {
_emitTag(rr, rr->metadataFile, TAG_MAX_STREAM);
rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR);
rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId));
}
rr->isRecording = true;
return GBARRIncrementStream(rr, false);
}
void GBARRStopRecording(struct GBARRContext* rr) {
if (!GBARRIsRecording(rr)) {
return;
}
rr->isRecording = false;
if (rr->movieStream) {
_emitEnd(rr, rr->movieStream);
rr->movieStream->close(rr->movieStream);
rr->movieStream = 0;
}
}
bool GBARRIsPlaying(struct GBARRContext* rr) {
return rr && rr->isPlaying;
}
bool GBARRIsRecording(struct GBARRContext* rr) {
return rr && rr->isRecording;
}
void GBARRNextFrame(struct GBARRContext* rr) {
if (!GBARRIsRecording(rr) && !GBARRIsPlaying(rr)) {
return;
}
if (GBARRIsPlaying(rr)) {
while (rr->peekedTag == TAG_INPUT) {
_readTag(rr, rr->movieStream);
GBALog(0, GBA_LOG_WARN, "[RR] Desync detected!");
}
if (rr->peekedTag == TAG_LAG) {
GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame marked in stream");
if (rr->inputThisFrame) {
GBALog(0, GBA_LOG_WARN, "[RR] Lag frame in stream does not match movie");
}
}
}
++rr->frames;
GBALog(0, GBA_LOG_DEBUG, "[RR] Frame: %u", rr->frames);
if (!rr->inputThisFrame) {
++rr->lagFrames;
GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame: %u", rr->lagFrames);
}
if (GBARRIsRecording(rr)) {
if (!rr->inputThisFrame) {
_emitTag(rr, rr->movieStream, TAG_LAG);
}
_emitTag(rr, rr->movieStream, TAG_FRAME);
rr->inputThisFrame = false;
} else {
if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) {
_streamEndReached(rr);
}
}
}
void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) {
if (!GBARRIsRecording(rr)) {
return;
}
if (keys != rr->currentInput) {
_emitTag(rr, rr->movieStream, TAG_INPUT);
rr->movieStream->write(rr->movieStream, &keys, sizeof(keys));
rr->currentInput = keys;
}
GBALog(0, GBA_LOG_DEBUG, "[RR] Input log: %03X", rr->currentInput);
rr->inputThisFrame = true;
}
uint16_t GBARRQueryInput(struct GBARRContext* rr) {
if (!GBARRIsPlaying(rr)) {
return 0;
}
if (rr->peekedTag == TAG_INPUT) {
_readTag(rr, rr->movieStream);
}
rr->inputThisFrame = true;
if (rr->currentInput == INVALID_INPUT) {
GBALog(0, GBA_LOG_WARN, "[RR] Stream did not specify input");
}
GBALog(0, GBA_LOG_DEBUG, "[RR] Input replay: %03X", rr->currentInput);
return rr->currentInput;
}
bool GBARRFinishSegment(struct GBARRContext* rr) {
if (rr->movieStream) {
if (!_emitEnd(rr, rr->movieStream)) {
return false;
}
}
return GBARRIncrementStream(rr, false);
}
bool GBARRSkipSegment(struct GBARRContext* rr) {
rr->nextTime = 0;
while (_readTag(rr, rr->movieStream) != TAG_EOF);
if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) {
_streamEndReached(rr);
return false;
}
return true;
}
bool GBARRMarkRerecord(struct GBARRContext* rr) {
++rr->rrCount;
rr->metadataFile->seek(rr->metadataFile, rr->rrCountOffset, SEEK_SET);
rr->metadataFile->write(rr->metadataFile, &rr->rrCount, sizeof(rr->rrCount));
return true;
}
bool _emitMagic(struct GBARRContext* rr, struct VFile* vf) {
UNUSED(rr);
return vf->write(vf, BINARY_MAGIC, 4) == 4;
}
bool _verifyMagic(struct GBARRContext* rr, struct VFile* vf) {
UNUSED(rr);
char buffer[4];
if (vf->read(vf, buffer, sizeof(buffer)) != sizeof(buffer)) {
return false;
}
if (memcmp(buffer, BINARY_MAGIC, sizeof(buffer)) != 0) {
return false;
}
return true;
}
enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) {
if (!rr || !vf) {
return TAG_EOF;
}
enum GBARRTag tag = rr->peekedTag;
switch (tag) {
case TAG_INPUT:
vf->read(vf, &rr->currentInput, sizeof(uint16_t));
break;
case TAG_PREVIOUSLY:
vf->read(vf, &rr->previously, sizeof(rr->previously));
break;
case TAG_NEXT_TIME:
vf->read(vf, &rr->nextTime, sizeof(rr->nextTime));
break;
case TAG_MAX_STREAM:
vf->read(vf, &rr->maxStreamId, sizeof(rr->maxStreamId));
break;
case TAG_FRAME_COUNT:
vf->read(vf, &rr->frames, sizeof(rr->frames));
break;
case TAG_LAG_COUNT:
vf->read(vf, &rr->lagFrames, sizeof(rr->lagFrames));
break;
case TAG_RR_COUNT:
vf->read(vf, &rr->rrCount, sizeof(rr->rrCount));
break;
case TAG_INIT_EX_NIHILO:
rr->initFrom = INIT_EX_NIHILO;
break;
case TAG_INIT_FROM_SAVEGAME:
rr->initFrom = INIT_FROM_SAVEGAME;
break;
case TAG_INIT_FROM_SAVESTATE:
rr->initFrom = INIT_FROM_SAVESTATE;
break;
case TAG_INIT_FROM_BOTH:
rr->initFrom = INIT_FROM_BOTH;
break;
// To be spec'd
case TAG_AUTHOR:
case TAG_COMMENT:
break;
// Empty markers
case TAG_FRAME:
case TAG_LAG:
case TAG_BEGIN:
case TAG_END:
case TAG_INVALID:
case TAG_EOF:
break;
}
uint8_t tagBuffer;
if (vf->read(vf, &tagBuffer, 1) != 1) {
rr->peekedTag = TAG_EOF;
} else {
rr->peekedTag = tagBuffer;
}
if (rr->peekedTag == TAG_END) {
GBARRSkipSegment(rr);
}
return tag;
}
bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) {
enum GBARRTag readTag;
while ((readTag = _readTag(rr, vf)) != tag) {
if (readTag == TAG_EOF) {
return false;
}
}
return true;
}
bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) {
UNUSED(rr);
return vf->write(vf, &tag, sizeof(tag)) == sizeof(tag);
}
bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) {
if (!_verifyMagic(rr, vf)) {
return false;
}
while (_readTag(rr, vf) != TAG_EOF) {
switch (rr->peekedTag) {
case TAG_MAX_STREAM:
rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_CUR);
break;
case TAG_INIT_EX_NIHILO:
case TAG_INIT_FROM_SAVEGAME:
case TAG_INIT_FROM_SAVESTATE:
case TAG_INIT_FROM_BOTH:
rr->initFromOffset = vf->seek(vf, 0, SEEK_CUR);
break;
case TAG_RR_COUNT:
rr->rrCountOffset = vf->seek(vf, 0, SEEK_CUR);
break;
default:
break;
}
}
return true;
}
bool _emitEnd(struct GBARRContext* rr, struct VFile* vf) {
// TODO: Error check
_emitTag(rr, vf, TAG_END);
_emitTag(rr, vf, TAG_FRAME_COUNT);
vf->write(vf, &rr->frames, sizeof(rr->frames));
_emitTag(rr, vf, TAG_LAG_COUNT);
vf->write(vf, &rr->lagFrames, sizeof(rr->lagFrames));
_emitTag(rr, vf, TAG_NEXT_TIME);
uint32_t newStreamId = 0;
vf->write(vf, &newStreamId, sizeof(newStreamId));
return true;
}
bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursive) {
if (rr->movieStream->seek(rr->movieStream, -sizeof(newStreamId) - 1, SEEK_END) < 0) {
return false;
}
uint8_t tagBuffer;
if (rr->movieStream->read(rr->movieStream, &tagBuffer, 1) != 1) {
return false;
}
if (tagBuffer != TAG_NEXT_TIME) {
return false;
}
if (rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId)) != sizeof(newStreamId)) {
return false;
}
if (recursive) {
if (rr->movieStream->seek(rr->movieStream, 0, SEEK_SET) < 0) {
return false;
}
if (!_verifyMagic(rr, rr->movieStream)) {
return false;
}
_readTag(rr, rr->movieStream);
if (_readTag(rr, rr->movieStream) != TAG_PREVIOUSLY) {
return false;
}
if (rr->previously == 0) {
return true;
}
uint32_t currentStreamId = rr->streamId;
if (!GBARRLoadStream(rr, rr->previously)) {
return false;
}
return _markStreamNext(rr, currentStreamId, rr->previously);
}
return true;
}
void _streamEndReached(struct GBARRContext* rr) {
if (!GBARRIsPlaying(rr)) {
return;
}
uint32_t endStreamId = rr->streamId;
GBARRStopPlaying(rr);
if (rr->autorecord) {
rr->isRecording = true;
GBARRLoadStream(rr, endStreamId);
GBARRIncrementStream(rr, false);
}
}
struct VFile* _openSavedata(struct GBARRContext* rr, int flags) {
return rr->streamDir->openFile(rr->streamDir, "movie.sav", flags);
}
struct VFile* _openSavestate(struct GBARRContext* rr, int flags) {
return rr->streamDir->openFile(rr->streamDir, "movie.ssm", flags);
rr->destroy(rr);
}

View File

@ -8,8 +8,8 @@
#include "util/common.h"
struct GBA;
struct VDir;
#include "gba/serialize.h"
struct VFile;
enum GBARRInitFrom {
@ -19,95 +19,39 @@ enum GBARRInitFrom {
INIT_FROM_BOTH = 3,
};
enum GBARRTag {
// Playback tags
TAG_INVALID = 0x00,
TAG_INPUT = 0x01,
TAG_FRAME = 0x02,
TAG_LAG = 0x03,
// Stream chunking tags
TAG_BEGIN = 0x10,
TAG_END = 0x11,
TAG_PREVIOUSLY = 0x12,
TAG_NEXT_TIME = 0x13,
TAG_MAX_STREAM = 0x14,
// Recording information tags
TAG_FRAME_COUNT = 0x20,
TAG_LAG_COUNT = 0x21,
TAG_RR_COUNT = 0x22,
TAG_INIT = 0x24,
TAG_INIT_EX_NIHILO = 0x24 | INIT_EX_NIHILO,
TAG_INIT_FROM_SAVEGAME = 0x24 | INIT_FROM_SAVEGAME,
TAG_INIT_FROM_SAVESTATE = 0x24 | INIT_FROM_SAVESTATE,
TAG_INIT_FROM_BOTH = 0x24 | INIT_FROM_BOTH,
// User metadata tags
TAG_AUTHOR = 0x30,
TAG_COMMENT = 0x31,
TAG_EOF = INT_MAX
};
struct GBARRContext {
// Playback state
bool isPlaying;
bool autorecord;
void (*destroy)(struct GBARRContext*);
// Recording state
bool isRecording;
bool inputThisFrame;
bool (*startPlaying)(struct GBARRContext*, bool autorecord);
void (*stopPlaying)(struct GBARRContext*);
bool (*startRecording)(struct GBARRContext*);
void (*stopRecording)(struct GBARRContext*);
bool (*isPlaying)(const struct GBARRContext*);
bool (*isRecording)(const struct GBARRContext*);
void (*nextFrame)(struct GBARRContext*);
void (*logInput)(struct GBARRContext*, uint16_t input);
uint16_t (*queryInput)(struct GBARRContext*);
void (*stateSaved)(struct GBARRContext*, struct GBASerializedState*);
void (*stateLoaded)(struct GBARRContext*, const struct GBASerializedState*);
struct VFile* (*openSavedata)(struct GBARRContext* mgm, int flags);
struct VFile* (*openSavestate)(struct GBARRContext* mgm, int flags);
// Metadata
uint32_t frames;
uint32_t lagFrames;
uint32_t streamId;
uint32_t maxStreamId;
off_t maxStreamIdOffset;
enum GBARRInitFrom initFrom;
off_t initFromOffset;
uint32_t rrCount;
off_t rrCountOffset;
struct VFile* savedata;
// Streaming state
struct VDir* streamDir;
struct VFile* metadataFile;
struct VFile* movieStream;
uint16_t currentInput;
enum GBARRTag peekedTag;
uint32_t nextTime;
uint32_t previously;
};
void GBARRContextCreate(struct GBA*);
void GBARRContextDestroy(struct GBA*);
void GBARRSaveState(struct GBA*);
void GBARRLoadState(struct GBA*);
void GBARRDestroy(struct GBARRContext*);
bool GBARRInitStream(struct GBARRContext*, struct VDir*);
bool GBARRReinitStream(struct GBARRContext*, enum GBARRInitFrom);
bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId);
bool GBARRIncrementStream(struct GBARRContext*, bool recursive);
bool GBARRFinishSegment(struct GBARRContext*);
bool GBARRSkipSegment(struct GBARRContext*);
bool GBARRMarkRerecord(struct GBARRContext*);
bool GBARRStartPlaying(struct GBARRContext*, bool autorecord);
void GBARRStopPlaying(struct GBARRContext*);
bool GBARRStartRecording(struct GBARRContext*);
void GBARRStopRecording(struct GBARRContext*);
bool GBARRIsPlaying(struct GBARRContext*);
bool GBARRIsRecording(struct GBARRContext*);
void GBARRNextFrame(struct GBARRContext*);
void GBARRLogInput(struct GBARRContext*, uint16_t input);
uint16_t GBARRQueryInput(struct GBARRContext*);
void GBARRInitRecord(struct GBA*);
void GBARRInitPlay(struct GBA*);
#endif

View File

@ -10,6 +10,8 @@
#include "gba/cheats.h"
#include "gba/serialize.h"
#include "gba/supervisor/config.h"
#include "gba/rr/mgm.h"
#include "gba/rr/vbm.h"
#include "debugger/debugger.h"
@ -117,6 +119,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
struct GBACheatDevice cheatDevice;
struct GBAThread* threadContext = context;
struct ARMComponent* components[GBA_COMPONENT_MAX] = {};
struct GBARRContext* movie = 0;
int numComponents = GBA_COMPONENT_MAX;
#if !defined(_WIN32) && defined(USE_PTHREADS)
@ -131,6 +134,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
gba.sync = &threadContext->sync;
threadContext->gba = &gba;
gba.logLevel = threadContext->logLevel;
gba.logHandler = threadContext->logHandler;
gba.stream = threadContext->stream;
gba.idleOptimization = threadContext->idleOptimization;
#ifdef USE_PTHREADS
pthread_setspecific(_contextKey, threadContext);
@ -170,7 +175,43 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
}
}
if (threadContext->movie) {
struct VDir* movieDir = VDirOpen(threadContext->movie);
#ifdef USE_LIBZIP
if (!movieDir) {
movieDir = VDirOpenZip(threadContext->movie, 0);
}
#endif
if (movieDir) {
struct GBAMGMContext* mgm = malloc(sizeof(*mgm));
GBAMGMContextCreate(mgm);
if (!GBAMGMSetStream(mgm, movieDir)) {
mgm->d.destroy(&mgm->d);
} else {
movie = &mgm->d;
}
} else {
struct VFile* movieFile = VFileOpen(threadContext->movie, O_RDONLY);
if (movieFile) {
struct GBAVBMContext* vbm = malloc(sizeof(*vbm));
GBAVBMContextCreate(vbm);
if (!GBAVBMSetStream(vbm, movieFile)) {
vbm->d.destroy(&vbm->d);
} else {
movie = &vbm->d;
}
}
}
}
ARMReset(&cpu);
if (movie) {
gba.rr = movie;
movie->startPlaying(movie, false);
GBARRInitPlay(&gba);
}
if (threadContext->skipBios) {
GBASkipBIOS(&cpu);
}
@ -256,6 +297,11 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
GBACheatDeviceDestroy(&cheatDevice);
}
if (movie) {
movie->destroy(movie);
free(movie);
}
threadContext->sync.videoFrameOn = false;
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
ConditionWake(&threadContext->sync.audioRequiredCond);
@ -295,12 +341,12 @@ void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread*
} else {
threadContext->rom = VFileOpen(args->fname, O_RDONLY);
threadContext->gameDir = 0;
#if ENABLE_LIBZIP
#if USE_LIBZIP
if (!threadContext->gameDir) {
threadContext->gameDir = VDirOpenZip(args->fname, 0);
}
#endif
#if ENABLE_LZMA
#if USE_LZMA
if (!threadContext->gameDir) {
threadContext->gameDir = VDirOpen7z(args->fname, 0);
}
@ -309,6 +355,7 @@ void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread*
threadContext->fname = args->fname;
threadContext->patch = VFileOpen(args->patch, O_RDONLY);
threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY);
threadContext->movie = args->movie;
}
bool GBAThreadStart(struct GBAThread* threadContext) {

View File

@ -20,7 +20,6 @@ struct GBACheatSet;
struct GBAOptions;
typedef void (*ThreadCallback)(struct GBAThread* threadContext);
typedef void (*LogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args);
enum ThreadState {
THREAD_INITIALIZED = -1,
@ -49,11 +48,6 @@ struct GBASync {
Mutex audioBufferMutex;
};
struct GBAAVStream {
void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
void (*postAudioFrame)(struct GBAAVStream*, int32_t left, int32_t right);
};
struct GBAThread {
// Output
enum ThreadState state;
@ -72,6 +66,7 @@ struct GBAThread {
struct VFile* patch;
struct VFile* cheatsFile;
const char* fname;
const char* movie;
int activeKeys;
struct GBAAVStream* stream;
struct Configuration* overrides;
@ -94,7 +89,7 @@ struct GBAThread {
enum ThreadState savedState;
int interruptDepth;
LogHandler logHandler;
GBALogHandler logHandler;
int logLevel;
ThreadCallback startCallback;
ThreadCallback cleanCallback;

View File

@ -235,7 +235,7 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, un
}
void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) {
void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) {
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
memcpy(state->oam, video->oam.raw, SIZE_OAM);
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
@ -249,7 +249,7 @@ void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state)
state->video.frameCounter = video->frameCounter;
}
void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state) {
void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) {
memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
int i;
for (i = 0; i < SIZE_OAM; i += 2) {

View File

@ -201,7 +201,7 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles);
void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value);
struct GBASerializedState;
void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state);
void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state);
void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state);
void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state);
#endif

View File

@ -34,8 +34,8 @@
static const struct option _options[] = {
{ "bios", required_argument, 0, 'b' },
{ "cheats", required_argument, 0, 'c' },
{ "dirmode", required_argument, 0, 'D' },
{ "cheats", required_argument, 0, 'c' },
{ "dirmode", required_argument, 0, 'D' },
{ "frameskip", required_argument, 0, 's' },
#ifdef USE_CLI_DEBUGGER
{ "debug", no_argument, 0, 'd' },
@ -43,6 +43,7 @@ static const struct option _options[] = {
#ifdef USE_GDB_STUB
{ "gdb", no_argument, 0, 'g' },
#endif
{ "movie", required_argument, 0, 'v' },
{ "patch", required_argument, 0, 'p' },
{ 0, 0, 0, 0 }
};
@ -52,7 +53,7 @@ bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int o
bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, struct SubParser* subparser) {
int ch;
char options[64] =
"b:c:Dl:p:s:"
"b:c:Dl:p:s:v:"
#ifdef USE_CLI_DEBUGGER
"d"
#endif
@ -101,6 +102,9 @@ bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int arg
case 's':
GBAConfigSetDefaultValue(config, "frameskip", optarg);
break;
case 'v':
opts->movie = strdup(optarg);
break;
default:
if (subparser) {
if (!subparser->parse(subparser, config, ch, optarg)) {
@ -125,6 +129,9 @@ void freeArguments(struct GBAArguments* opts) {
free(opts->patch);
opts->patch = 0;
free(opts->movie);
opts->movie = 0;
}
void initParserForGraphics(struct SubParser* parser, struct GraphicsOpts* opts) {
@ -211,6 +218,7 @@ void usage(const char* arg0, const char* extraOptions) {
#ifdef USE_GDB_STUB
puts(" -g, --gdb Start GDB session (default port 2345)");
#endif
puts(" -v, --movie FILE Play back a movie of recorded input");
puts(" -p, --patch FILE Apply a specified patch file when running");
puts(" -s, --frameskip N Skip every N frames");
if (extraOptions) {

View File

@ -26,6 +26,7 @@ struct GBAArguments {
char* patch;
char* cheatsFile;
bool dirmode;
char* movie;
enum DebuggerType debuggerType;
bool debugAtStart;

View File

@ -22,7 +22,7 @@
#include <libswscale/swscale.h>
static void _ffmpegPostVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
static void _ffmpegPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t right);
static void _ffmpegPostAudioFrame(struct GBAAVStream*, int16_t left, int16_t right);
enum {
PREFERRED_SAMPLE_RATE = 0x8000
@ -33,6 +33,7 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
encoder->d.postVideoFrame = _ffmpegPostVideoFrame;
encoder->d.postAudioFrame = _ffmpegPostAudioFrame;
encoder->d.postAudioBuffer = 0;
encoder->audioCodec = 0;
encoder->videoCodec = 0;
@ -288,10 +289,18 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->videoFrame->height = encoder->video->height;
encoder->videoFrame->pts = 0;
encoder->scaleContext = sws_getContext(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS,
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
AV_PIX_FMT_RGB565,
#else
AV_PIX_FMT_BGR555,
#endif
#else
#ifndef USE_LIBAV
AV_PIX_FMT_0BGR32,
#else
AV_PIX_FMT_BGR32,
#endif
#endif
encoder->videoFrame->width, encoder->videoFrame->height, encoder->video->pix_fmt,
SWS_POINT, 0, 0, 0);
@ -349,7 +358,7 @@ bool FFmpegEncoderIsOpen(struct FFmpegEncoder* encoder) {
return !!encoder->context;
}
void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int16_t left, int16_t right) {
struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
if (!encoder->context || !encoder->audioCodec) {
return;
@ -416,7 +425,7 @@ void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer*
uint8_t* pixels;
unsigned stride;
renderer->getPixels(renderer, &stride, (void**) &pixels);
stride *= 4;
stride *= BYTES_PER_PIXEL;
AVPacket packet;

View File

@ -8,13 +8,14 @@
#include "gba/video.h"
static void _magickPostVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
static void _magickPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t right);
static void _magickPostAudioFrame(struct GBAAVStream*, int16_t left, int16_t right);
void ImageMagickGIFEncoderInit(struct ImageMagickGIFEncoder* encoder) {
encoder->wand = 0;
encoder->d.postVideoFrame = _magickPostVideoFrame;
encoder->d.postAudioFrame = _magickPostAudioFrame;
encoder->d.postAudioBuffer = 0;
encoder->frameskip = 2;
}
@ -70,7 +71,7 @@ static void _magickPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRen
++encoder->currentFrame;
}
static void _magickPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
static void _magickPostAudioFrame(struct GBAAVStream* stream, int16_t left, int16_t right) {
UNUSED(stream);
UNUSED(left);
UNUSED(right);

View File

@ -0,0 +1,353 @@
/* 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 "libretro.h"
#include "gba/gba.h"
#include "gba/renderers/video-software.h"
#include "gba/serialize.h"
#include "gba/supervisor/overrides.h"
#include "gba/video.h"
#include "util/vfs.h"
#define SAMPLES 1024
static retro_environment_t environCallback;
static retro_video_refresh_t videoCallback;
static retro_audio_sample_batch_t audioCallback;
static retro_input_poll_t inputPollCallback;
static retro_input_state_t inputCallback;
static retro_log_printf_t logCallback;
static void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
static void _postAudioBuffer(struct GBAAVStream*, struct GBAAudio* audio);
static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
static struct GBA gba;
static struct ARMCore cpu;
static struct GBAVideoSoftwareRenderer renderer;
static struct VFile* rom;
static void* data;
static struct VFile* save;
static void* savedata;
static struct GBAAVStream stream;
unsigned retro_api_version(void) {
return RETRO_API_VERSION;
}
void retro_set_environment(retro_environment_t env) {
environCallback = env;
}
void retro_set_video_refresh(retro_video_refresh_t video) {
videoCallback = video;
}
void retro_set_audio_sample(retro_audio_sample_t audio) {
UNUSED(audio);
}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t audioBatch) {
audioCallback = audioBatch;
}
void retro_set_input_poll(retro_input_poll_t inputPoll) {
inputPollCallback = inputPoll;
}
void retro_set_input_state(retro_input_state_t input) {
inputCallback = input;
}
void retro_get_system_info(struct retro_system_info* info) {
info->need_fullpath = false;
info->valid_extensions = "gba";
info->library_version = PROJECT_VERSION;
info->library_name = PROJECT_NAME;
info->block_extract = false;
}
void retro_get_system_av_info(struct retro_system_av_info* info) {
info->geometry.base_width = VIDEO_HORIZONTAL_PIXELS;
info->geometry.base_height = VIDEO_VERTICAL_PIXELS;
info->geometry.max_width = VIDEO_HORIZONTAL_PIXELS;
info->geometry.max_height = VIDEO_VERTICAL_PIXELS;
info->timing.fps = GBA_ARM7TDMI_FREQUENCY / (float) VIDEO_TOTAL_LENGTH;
info->timing.sample_rate = 32768;
}
void retro_init(void) {
enum retro_pixel_format fmt;
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
fmt = RETRO_PIXEL_FORMAT_RGB565;
#else
#warning This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
fmt = RETRO_PIXEL_FORMAT_0RGB1555;
#endif
#else
#warning This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
fmt = RETRO_PIXEL_FORMAT_XRGB8888;
#endif
environCallback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt);
struct retro_input_descriptor inputDescriptors[] = {
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" }
};
environCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors);
// TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported
// TODO: RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE
struct retro_log_callback log;
if (environCallback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) {
logCallback = log.log;
} else {
logCallback = 0;
}
stream.postAudioFrame = 0;
stream.postAudioBuffer = _postAudioBuffer;
stream.postVideoFrame = _postVideoFrame;
GBACreate(&gba);
ARMSetComponents(&cpu, &gba.d, 0, 0);
ARMInit(&cpu);
gba.logLevel = 0; // TODO: Settings
gba.logHandler = GBARetroLog;
gba.stream = &stream;
gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
rom = 0;
GBAVideoSoftwareRendererCreate(&renderer);
renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
renderer.outputBufferStride = 256;
GBAVideoAssociateRenderer(&gba.video, &renderer.d);
GBAAudioResizeBuffer(&gba.audio, SAMPLES);
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_set_rates(gba.audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
#endif
}
void retro_deinit(void) {
GBADestroy(&gba);
}
void retro_run(void) {
int keys;
gba.keySource = &keys;
inputPollCallback();
keys = 0;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) << 0;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) << 1;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) << 2;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) << 3;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) << 4;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) << 5;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) << 6;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) << 7;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) << 8;
keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) << 9;
int frameCount = gba.video.frameCounter;
while (gba.video.frameCounter == frameCount) {
ARMRunLoop(&cpu);
}
videoCallback(renderer.outputBuffer, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, BYTES_PER_PIXEL * 256);
}
void retro_reset(void) {
ARMReset(&cpu);
}
bool retro_load_game(const struct retro_game_info* game) {
if (game->data) {
data = malloc(game->size);
memcpy(data, game->data, game->size);
rom = VFileFromMemory(data, game->size);
} else {
data = 0;
rom = VFileOpen(game->path, O_RDONLY);
}
if (!rom) {
return false;
}
if (!GBAIsROM(rom)) {
return false;
}
savedata = malloc(SIZE_CART_FLASH1M);
save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
GBALoadROM(&gba, rom, save, game->path);
struct GBACartridgeOverride override;
const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom;
memcpy(override.id, &cart->id, sizeof(override.id));
if (GBAOverrideFind(0, &override)) {
GBAOverrideApply(&gba, &override);
}
ARMReset(&cpu);
return true;
}
void retro_unload_game(void) {
rom->close(rom);
rom = 0;
free(data);
data = 0;
save->close(save);
save = 0;
free(savedata);
savedata = 0;
}
size_t retro_serialize_size(void) {
return sizeof(struct GBASerializedState);
}
bool retro_serialize(void* data, size_t size) {
if (size != retro_serialize_size()) {
return false;
}
GBASerialize(&gba, data);
return true;
}
bool retro_unserialize(const void* data, size_t size) {
if (size != retro_serialize_size()) {
return false;
}
GBADeserialize(&gba, data);
return true;
}
void retro_cheat_reset(void) {
// TODO: Cheats
}
void retro_cheat_set(unsigned index, bool enabled, const char* code) {
// TODO: Cheats
UNUSED(index);
UNUSED(enabled);
UNUSED(code);
}
unsigned retro_get_region(void) {
return RETRO_REGION_NTSC; // TODO: This isn't strictly true
}
void retro_set_controller_port_device(unsigned port, unsigned device) {
UNUSED(port);
UNUSED(device);
}
bool retro_load_game_special(unsigned game_type, const struct retro_game_info* info, size_t num_info) {
UNUSED(game_type);
UNUSED(info);
UNUSED(num_info);
return false;
}
void* retro_get_memory_data(unsigned id) {
if (id != RETRO_MEMORY_SAVE_RAM) {
return 0;
}
return savedata;
}
size_t retro_get_memory_size(unsigned id) {
if (id != RETRO_MEMORY_SAVE_RAM) {
return 0;
}
switch (gba.memory.savedata.type) {
case SAVEDATA_AUTODETECT:
case SAVEDATA_FLASH1M:
return SIZE_CART_FLASH1M;
case SAVEDATA_FLASH512:
return SIZE_CART_FLASH512;
case SAVEDATA_EEPROM:
return SIZE_CART_EEPROM;
case SAVEDATA_SRAM:
return SIZE_CART_SRAM;
case SAVEDATA_FORCE_NONE:
return 0;
}
return 0;
}
void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
UNUSED(thread);
if (!logCallback) {
return;
}
char message[128];
vsnprintf(message, sizeof(message), format, args);
enum retro_log_level retroLevel = RETRO_LOG_INFO;
switch (level) {
case GBA_LOG_ALL:
case GBA_LOG_ERROR:
case GBA_LOG_FATAL:
retroLevel = RETRO_LOG_ERROR;
break;
case GBA_LOG_WARN:
retroLevel = RETRO_LOG_WARN;
break;
case GBA_LOG_INFO:
case GBA_LOG_GAME_ERROR:
case GBA_LOG_SWI:
retroLevel = RETRO_LOG_INFO;
break;
case GBA_LOG_DEBUG:
case GBA_LOG_STUB:
retroLevel = RETRO_LOG_DEBUG;
break;
}
logCallback(retroLevel, "%s\n", message);
}
static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) {
UNUSED(stream);
int16_t samples[SAMPLES * 2];
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
blip_read_samples(audio->left, samples, SAMPLES, true);
blip_read_samples(audio->right, samples + 1, SAMPLES, true);
#else
int16_t samplesR[SAMPLES];
GBAAudioCopy(audio, &samples[SAMPLES], samplesR, SAMPLES);
size_t i;
for (i = 0; i < SAMPLES; ++i) {
samples[i * 2] = samples[SAMPLES + i];
samples[i * 2 + 1] = samplesR[i];
}
#endif
audioCallback(samples, SAMPLES);
}
static void _postVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
UNUSED(stream);
void* pixels;
unsigned stride;
renderer->getPixels(renderer, &stride, &pixels);
videoCallback(pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, BYTES_PER_PIXEL * stride);
}

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@ void AudioProcessorQt::start() {
format.setSampleType(QAudioFormat::SignedInt);
m_audioOutput = new QAudioOutput(format, this);
m_audioOutput->setCategory("game");
}
m_device->setInput(input());

View File

@ -110,7 +110,7 @@ if(WIN32)
list(APPEND RESOURCES ${CMAKE_SOURCE_DIR}/res/mgba.rc)
endif()
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_FILES} ${AUDIO_SRC} ${RESOURCES})
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in)
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES}")
list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::OpenGL)
target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES})

View File

@ -271,7 +271,15 @@ void Painter::performDraw() {
}
}
glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_backing);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, m_backing);
#endif
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
#endif
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
if (m_context->sync.videoFrameWait) {
glFlush();

View File

@ -20,9 +20,10 @@ const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.431;
const qreal GBAKeyEditor::DPAD_WIDTH = 0.1;
const qreal GBAKeyEditor::DPAD_HEIGHT = 0.1;
GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, QWidget* parent)
GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const char* profile, QWidget* parent)
: QWidget(parent)
, m_type(type)
, m_profile(profile)
, m_controller(controller)
{
setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);
@ -163,6 +164,10 @@ void GBAKeyEditor::save() {
bindKey(m_keyL, GBA_KEY_L);
bindKey(m_keyR, GBA_KEY_R);
m_controller->saveConfiguration(m_type);
if (m_profile) {
m_controller->saveProfile(m_type, m_profile);
}
}
void GBAKeyEditor::lookupBinding(const GBAInputMap* map, KeyEditor* keyEditor, GBAKey key) {

View File

@ -26,7 +26,7 @@ class GBAKeyEditor : public QWidget {
Q_OBJECT
public:
GBAKeyEditor(InputController* controller, int type, QWidget* parent = nullptr);
GBAKeyEditor(InputController* controller, int type, const char* profile = nullptr, QWidget* parent = nullptr);
public slots:
void setAll();
@ -76,6 +76,7 @@ private:
QList<KeyEditor*>::iterator m_currentKey;
uint32_t m_type;
const char* m_profile;
InputController* m_controller;
QPicture m_background;

View File

@ -43,14 +43,16 @@ void GDBController::attach() {
if (m_gameController->isLoaded()) {
ARMDebuggerEnter(&m_gdbStub.d, DEBUGGER_ENTER_ATTACHED, 0);
} else {
connect(m_gameController, &GameController::gameStarted, [this] () {
disconnect(m_gameController);
QObject::disconnect(m_autoattach);
m_autoattach = connect(m_gameController, &GameController::gameStarted, [this] () {
QObject::disconnect(m_autoattach);
ARMDebuggerEnter(&m_gdbStub.d, DEBUGGER_ENTER_ATTACHED, 0);
});
}
}
void GDBController::detach() {
QObject::disconnect(m_autoattach);
if (!isAttached()) {
return;
}

View File

@ -45,6 +45,8 @@ private:
ushort m_port;
Address m_bindAddress;
QMetaObject::Connection m_autoattach;
};
}

View File

@ -236,12 +236,12 @@ void GameController::openGame() {
m_threadContext.stateDir = m_threadContext.gameDir;
} else {
m_threadContext.rom = VFileOpen(m_threadContext.fname, O_RDONLY);
#if ENABLE_LIBZIP
#if USE_LIBZIP
if (!m_threadContext.gameDir) {
m_threadContext.gameDir = VDirOpenZip(m_threadContext.fname, 0);
}
#endif
#if ENABLE_LZMA
#if USE_LZMA
if (!m_threadContext.gameDir) {
m_threadContext.gameDir = VDirOpen7z(m_threadContext.fname, 0);
}
@ -509,6 +509,7 @@ void GameController::setLuminanceValue(uint8_t value) {
break;
}
}
emit luminanceValueChanged(m_luxValue);
}
void GameController::setLuminanceLevel(int level) {

View File

@ -86,6 +86,8 @@ signals:
void gameFailed();
void stateLoaded(GBAThread*);
void luminanceValueChanged(int);
void postLog(int level, const QString& log);
public slots:
@ -116,6 +118,7 @@ public slots:
void reloadAudioDriver();
void setLuminanceValue(uint8_t value);
uint8_t luminanceValue() const { return m_luxValue; }
void setLuminanceLevel(int level);
void increaseLuminanceLevel() { setLuminanceLevel(m_luxLevel + 1); }
void decreaseLuminanceLevel() { setLuminanceLevel(m_luxLevel - 1); }

View File

@ -63,6 +63,7 @@ void InputController::setConfiguration(ConfigController* config) {
loadConfiguration(KEYBOARD);
#ifdef BUILD_SDL
loadConfiguration(SDL_BINDING_BUTTON);
loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
#endif
}
@ -70,11 +71,34 @@ void InputController::loadConfiguration(uint32_t type) {
GBAInputMapLoad(&m_inputMap, type, m_config->configuration());
}
void InputController::loadProfile(uint32_t type, const char* profile) {
GBAInputProfileLoad(&m_inputMap, type, m_config->configuration(), profile);
}
void InputController::saveConfiguration(uint32_t type) {
GBAInputMapSave(&m_inputMap, type, m_config->configuration());
m_config->write();
}
void InputController::saveProfile(uint32_t type, const char* profile) {
GBAInputProfileSave(&m_inputMap, type, m_config->configuration(), profile);
m_config->write();
}
const char* InputController::profileForType(uint32_t type) {
UNUSED(type);
#ifdef BUILD_SDL
if (type == SDL_BINDING_BUTTON) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_JoystickName(m_sdlEvents.joystick);
#else
return SDL_JoystickName(SDL_JoystickIndex(m_sdlEvents.joystick));
#endif
}
#endif
return 0;
}
GBAKey InputController::mapKeyboard(int key) const {
return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
}

View File

@ -36,7 +36,10 @@ public:
void setConfiguration(ConfigController* config);
void loadConfiguration(uint32_t type);
void loadProfile(uint32_t type, const char* profile);
void saveConfiguration(uint32_t type = KEYBOARD);
void saveProfile(uint32_t type, const char* profile);
const char* profileForType(uint32_t type);
GBAKey mapKeyboard(int key) const;

View File

@ -143,6 +143,7 @@ void OverrideView::gameStopped() {
m_ui.hwTilt->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.hwRumble->setEnabled(!m_ui.hwAutodetect->isChecked());
m_ui.hwAutodetect->setChecked(true);
m_ui.hwRTC->setChecked(false);
m_ui.hwGyro->setChecked(false);
m_ui.hwLight->setChecked(false);
@ -150,7 +151,9 @@ void OverrideView::gameStopped() {
m_ui.hwRumble->setChecked(false);
m_ui.idleLoop->setEnabled(true);
m_ui.idleLoop->clear();
m_ui.clear->setEnabled(false);
m_ui.save->setEnabled(false);
updateOverrides();
}

View File

@ -35,16 +35,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="clear">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="save">
<property name="enabled">

View File

@ -31,12 +31,17 @@ SensorView::SensorView(GameController* controller, QWidget* parent)
connect(m_ui.timeNow, &QPushButton::clicked, [controller, this] () {
m_ui.time->setDateTime(QDateTime::currentDateTime());
});
connect(m_controller, SIGNAL(luminanceValueChanged(int)), this, SLOT(luminanceValueChanged(int)));
}
void SensorView::setLuminanceValue(int value) {
bool oldState;
value = std::max(0, std::min(value, 255));
m_controller->setLuminanceValue(value);
}
void SensorView::luminanceValueChanged(int value) {
bool oldState;
oldState = m_ui.lightSpin->blockSignals(true);
m_ui.lightSpin->setValue(value);
m_ui.lightSpin->blockSignals(oldState);
@ -44,6 +49,4 @@ void SensorView::setLuminanceValue(int value) {
oldState = m_ui.lightSlide->blockSignals(true);
m_ui.lightSlide->setValue(value);
m_ui.lightSlide->blockSignals(oldState);
m_controller->setLuminanceValue(value);
}

View File

@ -23,6 +23,7 @@ public:
private slots:
void setLuminanceValue(int);
void luminanceValueChanged(int);
private:
Ui::SensorView m_ui;

View File

@ -256,7 +256,8 @@ void Window::openCheatsWindow() {
#ifdef BUILD_SDL
void Window::openGamepadWindow() {
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, SDL_BINDING_BUTTON);
const char* profile = m_inputController.profileForType(SDL_BINDING_BUTTON);
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, SDL_BINDING_BUTTON, profile);
connect(this, SIGNAL(shutdown()), keyEditor, SLOT(close()));
keyEditor->setAttribute(Qt::WA_DeleteOnClose);
keyEditor->show();
@ -714,22 +715,22 @@ void Window::setupMenu(QMenuBar* menubar) {
addControlledAction(toolsMenu, gdbWindow, "gdbWindow");
#endif
toolsMenu->addSeparator();
QAction* solarIncrease = new QAction(tr("Increase solar level"), toolsMenu);
QMenu* solarMenu = toolsMenu->addMenu(tr("Solar sensor"));
QAction* solarIncrease = new QAction(tr("Increase solar level"), solarMenu);
connect(solarIncrease, SIGNAL(triggered()), m_controller, SLOT(increaseLuminanceLevel()));
addControlledAction(toolsMenu, solarIncrease, "increaseLuminanceLevel");
addControlledAction(solarMenu, solarIncrease, "increaseLuminanceLevel");
QAction* solarDecrease = new QAction(tr("Decrease solar level"), toolsMenu);
QAction* solarDecrease = new QAction(tr("Decrease solar level"), solarMenu);
connect(solarDecrease, SIGNAL(triggered()), m_controller, SLOT(decreaseLuminanceLevel()));
addControlledAction(toolsMenu, solarDecrease, "decreaseLuminanceLevel");
addControlledAction(solarMenu, solarDecrease, "decreaseLuminanceLevel");
QAction* maxSolar = new QAction(tr("Brightest solar level"), toolsMenu);
QAction* maxSolar = new QAction(tr("Brightest solar level"), solarMenu);
connect(maxSolar, &QAction::triggered, [this]() { m_controller->setLuminanceLevel(10); });
addControlledAction(toolsMenu, maxSolar, "maxLuminanceLevel");
addControlledAction(solarMenu, maxSolar, "maxLuminanceLevel");
QAction* minSolar = new QAction(tr("Darkest solar level"), toolsMenu);
QAction* minSolar = new QAction(tr("Darkest solar level"), solarMenu);
connect(minSolar, &QAction::triggered, [this]() { m_controller->setLuminanceLevel(0); });
addControlledAction(toolsMenu, minSolar, "minLuminanceLevel");
addControlledAction(solarMenu, minSolar, "minLuminanceLevel");
toolsMenu->addSeparator();
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())), "settings");

View File

@ -38,6 +38,7 @@ if(BUILD_RASPI)
set(EGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fgnu89-inline")
add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${MAIN_SRC} ${EGL_MAIN_SRC})
set_target_properties(${BINARY_NAME}-rpi PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}")
target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${EGL_LIBRARY})
install(TARGETS ${BINARY_NAME}-rpi DESTINATION bin COMPONENT ${BINARY_NAME}-rpi)
endif()
@ -52,6 +53,7 @@ else()
endif()
add_executable(${BINARY_NAME}-sdl WIN32 ${PLATFORM_SRC} ${MAIN_SRC})
set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}")
target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY})
set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME})
install(TARGETS ${BINARY_NAME}-sdl DESTINATION bin COMPONENT ${BINARY_NAME}-sdl)

View File

@ -59,7 +59,7 @@ bool GBASDLInit(struct SDLSoftwareRenderer* renderer) {
#endif
#endif
renderer->d.outputBuffer = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
renderer->d.outputBuffer = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
glGenTextures(1, &renderer->tex);
glBindTexture(GL_TEXTURE_2D, renderer->tex);

View File

@ -78,7 +78,14 @@ void GBASDLInitBindings(struct GBAInputMap* inputMap) {
void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) {
GBAInputMapLoad(context->bindings, SDL_BINDING_KEY, config);
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
if (context->joystick) {
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
#if SDL_VERSION_ATLEAST(2, 0, 0)
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(context->joystick));
#else
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, SDL_JoystickName(SDL_JoystickIndex(context->joystick)));
#endif
}
}
void GBASDLDeinitEvents(struct GBASDLEvents* context) {
@ -140,14 +147,6 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
GBARewind(context, 10);
GBAThreadContinue(context);
return;
case SDLK_ESCAPE:
GBAThreadInterrupt(context);
if (context->gba->rr) {
GBARRStopPlaying(context->gba->rr);
GBARRStopRecording(context->gba->rr);
}
GBAThreadContinue(context);
return;
default:
if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) {
switch (event->keysym.sym) {
@ -169,31 +168,6 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
case SDLK_r:
GBAThreadReset(context);
break;
case SDLK_t:
if (context->stateDir) {
GBAThreadInterrupt(context);
GBARRContextCreate(context->gba);
if (!GBARRIsRecording(context->gba->rr)) {
GBARRStopPlaying(context->gba->rr);
GBARRInitStream(context->gba->rr, context->stateDir);
GBARRReinitStream(context->gba->rr, INIT_EX_NIHILO);
GBARRStartRecording(context->gba->rr);
GBARRSaveState(context->gba);
}
GBAThreadContinue(context);
}
break;
case SDLK_y:
if (context->stateDir) {
GBAThreadInterrupt(context);
GBARRContextCreate(context->gba);
GBARRStopRecording(context->gba->rr);
GBARRInitStream(context->gba->rr, context->stateDir);
GBARRStartPlaying(context->gba->rr, false);
GBARRLoadState(context->gba);
GBAThreadContinue(context);
}
break;
default:
break;
}

View File

@ -1,8 +1,9 @@
# Copyright (c) 2013-2014 Jeffrey Pfau
# 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/.
#ifdef __ARM_NEON
# r0: Destination
# r1: Source
# r2: Number of words to copy as halfwords
@ -13,28 +14,22 @@ mov r8, r0
mov r9, r1
mov r10, r2
.L0:
tst r10, #7
tst r10, #15
beq .L1
ldr r0, [r9], #4
strh r0, [r8], #2
sub r10, #1
b .L0
.L1:
ldmia r9!, {r0-r7}
strh r0, [r8], #2
strh r1, [r8], #2
strh r2, [r8], #2
strh r3, [r8], #2
strh r4, [r8], #2
strh r5, [r8], #2
strh r6, [r8], #2
strh r7, [r8], #2
subs r10, #8
vld4.16 {d0, d1, d2, d3}, [r9]!
vld4.16 {d4, d5, d6, d7}, [r9]!
vst2.16 {d0, d2}, [r8]!
vst2.16 {d4, d6}, [r8]!
subs r10, #16
bne .L1
pop {r4-r10}
bx lr
#ifdef __ARM_NEON
# r0: Destination
# r1: Source
# r2: Width
@ -97,3 +92,5 @@ bne .n40
pop {r4-r7}
bx lr
#endif
.section .note.GNU-stack,"",%progbits

View File

@ -253,7 +253,7 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc
struct timeval tv;
tv.tv_sec = timeoutMillis / 1000;
tv.tv_usec = (timeoutMillis % 1000) * 1000;
int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
int result = select(maxFd + 1, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
int r = 0;
int w = 0;
int e = 0;

View File

@ -378,3 +378,15 @@ const char* _vdeName(struct VDirEntry* vde) {
}
return 0;
}
ssize_t VFileReadline(struct VFile* vf, char* buffer, size_t size) {
size_t bytesRead = 0;
while (bytesRead < size - 1) {
size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
bytesRead += newRead;
if (!newRead || buffer[bytesRead] == '\n') {
break;
}
}
return buffer[bytesRead] = '\0';
}

View File

@ -38,18 +38,21 @@ struct VDir {
struct VFile* VFileOpen(const char* path, int flags);
struct VFile* VFileFromFD(int fd);
struct VFile* VFileFromMemory(void* mem, size_t size);
struct VDir* VDirOpen(const char* path);
#ifdef ENABLE_LIBZIP
#ifdef USE_LIBZIP
struct VDir* VDirOpenZip(const char* path, int flags);
#endif
#ifdef ENABLE_LZMA
#ifdef USE_LZMA
struct VDir* VDirOpen7z(const char* path, int flags);
#endif
struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode);
struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode);
ssize_t VFileReadline(struct VFile* vf, char* buffer, size_t size);
#endif

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/vfs.h"
#ifdef ENABLE_LZMA
#ifdef USE_LZMA
#include "util/string.h"
@ -51,7 +51,6 @@ struct VFile7z {
static bool _vf7zClose(struct VFile* vf);
static off_t _vf7zSeek(struct VFile* vf, off_t offset, int whence);
static ssize_t _vf7zRead(struct VFile* vf, void* buffer, size_t size);
static ssize_t _vf7zReadline(struct VFile* vf, char* buffer, size_t size);
static ssize_t _vf7zWrite(struct VFile* vf, const void* buffer, size_t size);
static void* _vf7zMap(struct VFile* vf, size_t size, int flags);
static void _vf7zUnmap(struct VFile* vf, void* memory, size_t size);
@ -142,16 +141,12 @@ off_t _vf7zSeek(struct VFile* vf, off_t offset, int whence) {
return -1;
}
if (position <= vf7z->offset) {
vf7z->offset = position;
return position;
if (position > vf7z->size) {
return -1;
}
if (position <= vf7z->size) {
return vf7z->offset;
}
return -1;
vf7z->offset = position;
return position;
}
ssize_t _vf7zRead(struct VFile* vf, void* buffer, size_t size) {
@ -162,21 +157,10 @@ ssize_t _vf7zRead(struct VFile* vf, void* buffer, size_t size) {
}
memcpy(buffer, vf7z->outBuffer + vf7z->offset + vf7z->bufferOffset, size);
vf7z->offset += size;
return size;
}
ssize_t _vf7zReadline(struct VFile* vf, char* buffer, size_t size) {
size_t bytesRead = 0;
while (bytesRead < size - 1) {
size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
bytesRead += newRead;
if (!newRead || buffer[bytesRead] == '\n') {
break;
}
}
return buffer[bytesRead] = '\0';
}
ssize_t _vf7zWrite(struct VFile* vf, const void* buffer, size_t size) {
// TODO
UNUSED(vf);
@ -301,7 +285,7 @@ struct VFile* _vd7zOpenFile(struct VDir* vd, const char* path, int mode) {
vf->d.close = _vf7zClose;
vf->d.seek = _vf7zSeek;
vf->d.read = _vf7zRead;
vf->d.readline = _vf7zReadline;
vf->d.readline = VFileReadline;
vf->d.write = _vf7zWrite;
vf->d.map = _vf7zMap;
vf->d.unmap = _vf7zUnmap;

139
src/util/vfs/vfs-mem.c Normal file
View File

@ -0,0 +1,139 @@
/* 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 "util/vfs.h"
struct VFileMem {
struct VFile d;
void* mem;
size_t size;
size_t offset;
};
static bool _vfmClose(struct VFile* vf);
static off_t _vfmSeek(struct VFile* vf, off_t offset, int whence);
static ssize_t _vfmRead(struct VFile* vf, void* buffer, size_t size);
static ssize_t _vfmWrite(struct VFile* vf, const void* buffer, size_t size);
static void* _vfmMap(struct VFile* vf, size_t size, int flags);
static void _vfmUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfmTruncate(struct VFile* vf, size_t size);
static ssize_t _vfmSize(struct VFile* vf);
struct VFile* VFileFromMemory(void* mem, size_t size) {
if (!mem || !size) {
return 0;
}
struct VFileMem* vfm = malloc(sizeof(struct VFileMem));
if (!vfm) {
return 0;
}
vfm->mem = mem;
vfm->size = size;
vfm->offset = 0;
vfm->d.close = _vfmClose;
vfm->d.seek = _vfmSeek;
vfm->d.read = _vfmRead;
vfm->d.readline = VFileReadline;
vfm->d.write = _vfmWrite;
vfm->d.map = _vfmMap;
vfm->d.unmap = _vfmUnmap;
vfm->d.truncate = _vfmTruncate;
vfm->d.size = _vfmSize;
return &vfm->d;
}
bool _vfmClose(struct VFile* vf) {
struct VFileMem* vfm = (struct VFileMem*) vf;
vfm->mem = 0;
free(vfm);
return true;
}
off_t _vfmSeek(struct VFile* vf, off_t offset, int whence) {
struct VFileMem* vfm = (struct VFileMem*) vf;
size_t position;
switch (whence) {
case SEEK_SET:
position = offset;
break;
case SEEK_CUR:
if (offset < 0 && ((vfm->offset < (size_t) -offset) || (offset == INT_MIN))) {
return -1;
}
position = vfm->offset + offset;
break;
case SEEK_END:
if (offset < 0 && ((vfm->size < (size_t) -offset) || (offset == INT_MIN))) {
return -1;
}
position = vfm->size + offset;
break;
default:
return -1;
}
if (position > vfm->size) {
return -1;
}
vfm->offset = position;
return position;
}
ssize_t _vfmRead(struct VFile* vf, void* buffer, size_t size) {
struct VFileMem* vfm = (struct VFileMem*) vf;
if (size + vfm->offset >= vfm->size) {
size = vfm->size - vfm->offset;
}
memcpy(buffer, vfm->mem + vfm->offset, size);
vfm->offset += size;
return size;
}
ssize_t _vfmWrite(struct VFile* vf, const void* buffer, size_t size) {
struct VFileMem* vfm = (struct VFileMem*) vf;
if (size + vfm->offset >= vfm->size) {
size = vfm->size - vfm->offset;
}
memcpy(vfm->mem + vfm->offset, buffer, size);
vfm->offset += size;
return size;
}
void* _vfmMap(struct VFile* vf, size_t size, int flags) {
struct VFileMem* vfm = (struct VFileMem*) vf;
UNUSED(flags);
if (size > vfm->size) {
return 0;
}
return vfm->mem;
}
void _vfmUnmap(struct VFile* vf, void* memory, size_t size) {
UNUSED(vf);
UNUSED(memory);
UNUSED(size);
}
void _vfmTruncate(struct VFile* vf, size_t size) {
// TODO: Return value?
UNUSED(vf);
UNUSED(size);
}
ssize_t _vfmSize(struct VFile* vf) {
struct VFileMem* vfm = (struct VFileMem*) vf;
return vfm->size;
}

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/vfs.h"
#ifdef ENABLE_LIBZIP
#ifdef USE_LIBZIP
#include <zip.h>
@ -38,7 +38,6 @@ struct VFileZip {
static bool _vfzClose(struct VFile* vf);
static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
static ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size);
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);
@ -188,18 +187,6 @@ ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
return bytesRead;
}
ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
size_t bytesRead = 0;
while (bytesRead < size - 1) {
size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
bytesRead += newRead;
if (!newRead || buffer[bytesRead] == '\n') {
break;
}
}
return buffer[bytesRead] = '\0';
}
ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
// TODO
UNUSED(vf);
@ -296,7 +283,7 @@ struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
vfz->d.close = _vfzClose;
vfz->d.seek = _vfzSeek;
vfz->d.read = _vfzRead;
vfz->d.readline = _vfzReadline;
vfz->d.readline = VFileReadline;
vfz->d.write = _vfzWrite;
vfz->d.map = _vfzMap;
vfz->d.unmap = _vfzUnmap;