From 4a3bc9de18c7b4b409fe7f3daedd24da97ea3141 Mon Sep 17 00:00:00 2001 From: risingPhil Date: Mon, 29 Jul 2024 00:51:54 +0200 Subject: [PATCH] Add functionality to get and set event flags --- examples/Makefile | 4 + examples/gen2_addItem/main.cpp | 2 +- examples/gen2_getEventFlag/Makefile | 48 ++++++++++++ examples/gen2_getEventFlag/main.cpp | 79 ++++++++++++++++++++ examples/gen2_gsball/main.cpp | 2 +- examples/gen2_removeItem/main.cpp | 2 +- examples/gen2_setEventFlag/Makefile | 48 ++++++++++++ examples/gen2_setEventFlag/main.cpp | 111 ++++++++++++++++++++++++++++ include/gen2/Gen2GameReader.h | 9 +++ src/gen2/Gen2GameReader.cpp | 41 ++++++++++ 10 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 examples/gen2_getEventFlag/Makefile create mode 100644 examples/gen2_getEventFlag/main.cpp create mode 100644 examples/gen2_setEventFlag/Makefile create mode 100644 examples/gen2_setEventFlag/main.cpp diff --git a/examples/Makefile b/examples/Makefile index 5afc5b6..784df09 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -11,6 +11,8 @@ all: $(MAKE) -C gen2_gsball $(MAKECMDGOALS) $(MAKEOVERRIDES) $(MAKE) -C gen2_addItem $(MAKECMDGOALS) $(MAKEOVERRIDES) $(MAKE) -C gen2_removeItem $(MAKECMDGOALS) $(MAKEOVERRIDES) + $(MAKE) -C gen2_getEventFlag $(MAKECMDGOALS) $(MAKEOVERRIDES) + $(MAKE) -C gen2_setEventFlag $(MAKECMDGOALS) $(MAKEOVERRIDES) clean: $(MAKE) -C do_stuff_gen1 clean @@ -22,3 +24,5 @@ clean: $(MAKE) -C gen2_gsball clean $(MAKE) -C gen2_addItem clean $(MAKE) -C gen2_removeItem clean + $(MAKE) -C gen2_getEventFlag clean + $(MAKE) -C gen2_setEventFlag clean diff --git a/examples/gen2_addItem/main.cpp b/examples/gen2_addItem/main.cpp index 75904bc..53d3bd9 100644 --- a/examples/gen2_addItem/main.cpp +++ b/examples/gen2_addItem/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char** argv) { if(argc != 3) { - fprintf(stderr, "Usage: addDistributionPoke \n"); + fprintf(stderr, "Usage: gen2_addItem \n"); return 1; } diff --git a/examples/gen2_getEventFlag/Makefile b/examples/gen2_getEventFlag/Makefile new file mode 100644 index 0000000..7589ec7 --- /dev/null +++ b/examples/gen2_getEventFlag/Makefile @@ -0,0 +1,48 @@ +# # Compiler flags +CXXFLAGS := -std=c++11 -fno-rtti -fno-exceptions -fno-unwind-tables -Wall -Wextra -I $(CURDIR) -I $(CURDIR)/../../include -g -Os + +# Source files directory +SRC_DIR := . +# Build directory +BUILD_DIR := build + +# Source files (add more as needed) +SRCS := $(shell find $(SRC_DIR) -type f -name '*.cpp') +# Object files +OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS)) + +LIBS := -lpokemegb +ifeq ($(PNG_SUPPORT),1) + LIBS += -lpng +endif + +# Ensure necessary directories exist +# This function ensures the directory for the target exists +define make_directory + @mkdir -p $(dir $@) +endef + +# Target executable +TARGET := ../../gen2_getEventFlag + +# Phony targets +.PHONY: all clean + +# Default target +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) $^ -o $@ $(LIBS) -L ../../ + +# Rule to compile source files +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR) + $(make_directory) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +# Create the build directory if it doesn't exist +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# Clean rule +clean: + rm -rf $(BUILD_DIR) $(TARGET) \ No newline at end of file diff --git a/examples/gen2_getEventFlag/main.cpp b/examples/gen2_getEventFlag/main.cpp new file mode 100644 index 0000000..faae6ec --- /dev/null +++ b/examples/gen2_getEventFlag/main.cpp @@ -0,0 +1,79 @@ +#include "gen2/Gen2GameReader.h" +#include "gen1/Gen1Common.h" +#include "RomReader.h" +#include "SaveManager.h" +#include "utils.h" + +#include +#include +#include + +static uint16_t getFlagNumber() +{ + char input[100]; + printf("Enter event flag number: "); + if (fgets(input, sizeof(input), stdin) == NULL) + { + fprintf(stderr, "Error reading input\n"); + return 0; + } + // Remove the newline character if it exists + input[strcspn(input, "\n")] = '\0'; + + return (uint16_t)strtoul(input, 0, 10); +} + +int main(int argc, char** argv) +{ + if(argc != 3) + { + fprintf(stderr, "Usage: gen2_getEventFlag \n"); + return 1; + } + + uint8_t* romBuffer; + uint8_t* saveBuffer; + uint32_t romFileSize; + uint32_t saveFileSize; + + romBuffer = readFileIntoBuffer(argv[1], romFileSize); + if(!romBuffer) + { + fprintf(stderr, "ERROR: Couldn't read file %s\n", argv[1]); + return 1; + } + saveBuffer = readFileIntoBuffer(argv[2], saveFileSize); + if(!saveBuffer) + { + fprintf(stderr, "ERROR: Couldn't read file %s\n", argv[2]); + return 1; + } + + GameboyCartridgeHeader cartridgeHeader; + BufferBasedRomReader romReader(romBuffer, romFileSize); + BufferBasedSaveManager saveManager(saveBuffer, saveFileSize); + + readGameboyCartridgeHeader(romReader, cartridgeHeader); + + //check if we're dealing with gen 1 + const Gen1GameType gen1Type = gen1_determineGameType(cartridgeHeader); + const Gen2GameType gen2Type = gen2_determineGameType(cartridgeHeader); + if(gen1Type != Gen1GameType::INVALID) + { + fprintf(stderr, "ERROR: sorry, this tool only supports gen 2!\n"); + return 1; + } + else if(gen2Type == Gen2GameType::INVALID) + { + fprintf(stderr, "ERROR: this is not a Gen 2 pokémon game!\n"); + return 1; + } + + const uint16_t flagNumber = getFlagNumber(); + + Gen2GameReader gameReader(romReader, saveManager, gen2Type); + bool result = gameReader.getEventFlag(flagNumber); + printf("Flag %hu => %s\n", flagNumber, (result) ? "true" : "false"); + + return 0; +} \ No newline at end of file diff --git a/examples/gen2_gsball/main.cpp b/examples/gen2_gsball/main.cpp index 9ddf218..b29f817 100644 --- a/examples/gen2_gsball/main.cpp +++ b/examples/gen2_gsball/main.cpp @@ -15,7 +15,7 @@ int main(int argc, char** argv) { if(argc != 3) { - fprintf(stderr, "Usage: addDistributionPoke \n"); + fprintf(stderr, "Usage: gen2_gsball \n"); return 1; } diff --git a/examples/gen2_removeItem/main.cpp b/examples/gen2_removeItem/main.cpp index 0c65fbc..352ad83 100644 --- a/examples/gen2_removeItem/main.cpp +++ b/examples/gen2_removeItem/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char** argv) { if(argc != 3) { - fprintf(stderr, "Usage: addDistributionPoke \n"); + fprintf(stderr, "Usage: gen2_removeItem \n"); return 1; } diff --git a/examples/gen2_setEventFlag/Makefile b/examples/gen2_setEventFlag/Makefile new file mode 100644 index 0000000..f97f05d --- /dev/null +++ b/examples/gen2_setEventFlag/Makefile @@ -0,0 +1,48 @@ +# # Compiler flags +CXXFLAGS := -std=c++11 -fno-rtti -fno-exceptions -fno-unwind-tables -Wall -Wextra -I $(CURDIR) -I $(CURDIR)/../../include -g -Os + +# Source files directory +SRC_DIR := . +# Build directory +BUILD_DIR := build + +# Source files (add more as needed) +SRCS := $(shell find $(SRC_DIR) -type f -name '*.cpp') +# Object files +OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS)) + +LIBS := -lpokemegb +ifeq ($(PNG_SUPPORT),1) + LIBS += -lpng +endif + +# Ensure necessary directories exist +# This function ensures the directory for the target exists +define make_directory + @mkdir -p $(dir $@) +endef + +# Target executable +TARGET := ../../gen2_setEventFlag + +# Phony targets +.PHONY: all clean + +# Default target +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) $^ -o $@ $(LIBS) -L ../../ + +# Rule to compile source files +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR) + $(make_directory) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +# Create the build directory if it doesn't exist +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# Clean rule +clean: + rm -rf $(BUILD_DIR) $(TARGET) \ No newline at end of file diff --git a/examples/gen2_setEventFlag/main.cpp b/examples/gen2_setEventFlag/main.cpp new file mode 100644 index 0000000..50ff686 --- /dev/null +++ b/examples/gen2_setEventFlag/main.cpp @@ -0,0 +1,111 @@ +#include "gen2/Gen2GameReader.h" +#include "gen1/Gen1Common.h" +#include "RomReader.h" +#include "SaveManager.h" +#include "utils.h" + +#include +#include +#include + +static uint16_t getFlagNumber() +{ + char input[100]; + printf("Enter event flag number: "); + if (fgets(input, sizeof(input), stdin) == NULL) + { + fprintf(stderr, "Error reading input\n"); + return 0; + } + // Remove the newline character if it exists + input[strcspn(input, "\n")] = '\0'; + + return (uint16_t)strtoul(input, 0, 10); +} + +static uint16_t getFlagValue() +{ + char input[100]; + printf("Enter new value (0-1): "); + if (fgets(input, sizeof(input), stdin) == NULL) + { + fprintf(stderr, "Error reading input\n"); + return 0; + } + // Remove the newline character if it exists + input[strcspn(input, "\n")] = '\0'; + + return (uint16_t)strtoul(input, 0, 10); +} + +int main(int argc, char** argv) +{ + if(argc != 3) + { + fprintf(stderr, "Usage: gen2_setEventFlag \n"); + return 1; + } + + uint8_t* romBuffer; + uint8_t* saveBuffer; + uint32_t romFileSize; + uint32_t saveFileSize; + + romBuffer = readFileIntoBuffer(argv[1], romFileSize); + if(!romBuffer) + { + fprintf(stderr, "ERROR: Couldn't read file %s\n", argv[1]); + return 1; + } + saveBuffer = readFileIntoBuffer(argv[2], saveFileSize); + if(!saveBuffer) + { + fprintf(stderr, "ERROR: Couldn't read file %s\n", argv[2]); + return 1; + } + + GameboyCartridgeHeader cartridgeHeader; + BufferBasedRomReader romReader(romBuffer, romFileSize); + BufferBasedSaveManager saveManager(saveBuffer, saveFileSize); + + readGameboyCartridgeHeader(romReader, cartridgeHeader); + + //check if we're dealing with gen 1 + const Gen1GameType gen1Type = gen1_determineGameType(cartridgeHeader); + const Gen2GameType gen2Type = gen2_determineGameType(cartridgeHeader); + if(gen1Type != Gen1GameType::INVALID) + { + fprintf(stderr, "ERROR: sorry, this tool only supports gen 2!\n"); + return 1; + } + else if(gen2Type == Gen2GameType::INVALID) + { + fprintf(stderr, "ERROR: this is not a Gen 2 pokémon game!\n"); + return 1; + } + + const uint16_t flagNumber = getFlagNumber(); + const uint16_t flagValue = getFlagValue(); + + if(flagValue != 0 && flagValue != 1) + { + fprintf(stderr, "ERROR: wrong value %hu\n", flagValue); + return 1; + } + + Gen2GameReader gameReader(romReader, saveManager, gen2Type); + gameReader.setEventFlag(flagNumber, static_cast(flagValue)); + gameReader.finishSave(); + printf("Flag %hu => %s\n", flagNumber, (static_cast(flagValue)) ? "true" : "false"); + + FILE* f = fopen(argv[2], "w"); + fwrite(saveBuffer, 1, saveFileSize, f); + fclose(f); + + free(romBuffer); + free(saveBuffer); + romBuffer = 0; + saveBuffer = 0; + + return 0; +} \ No newline at end of file diff --git a/include/gen2/Gen2GameReader.h b/include/gen2/Gen2GameReader.h index 02a5e06..b95b233 100644 --- a/include/gen2/Gen2GameReader.h +++ b/include/gen2/Gen2GameReader.h @@ -183,6 +183,15 @@ public: * @brief Unlocks the GS Ball event in Pokemon crystal */ void unlockGsBallEvent(); + + /** + * @brief Retrieves the value of the given event flag + */ + bool getEventFlag(uint16_t flagNumber); + /** + * @brief Sets the value of the given event flag number + */ + void setEventFlag(uint16_t flagNumber, bool enabled); protected: private: IRomReader &romReader_; diff --git a/src/gen2/Gen2GameReader.cpp b/src/gen2/Gen2GameReader.cpp index 0188c06..4db0409 100644 --- a/src/gen2/Gen2GameReader.cpp +++ b/src/gen2/Gen2GameReader.cpp @@ -9,6 +9,9 @@ #define CRYSTAL_PICS_FIX 0x36 #define CRYSTAL_BANK_PICS_1 72 +#define EVENT_FLAGS_OFFSET_GOLDSILVER 0x261F +#define EVENT_FLAGS_OFFSET_CRYSTAL 0x2600 + static const uint8_t crystalPicsBanks[] = { CRYSTAL_BANK_PICS_1 + 0, CRYSTAL_BANK_PICS_1 + 1, @@ -696,4 +699,42 @@ void Gen2GameReader::unlockGsBallEvent() saveManager_.writeByte(0xB); saveManager_.seek(0x3E44); saveManager_.writeByte(0xB); +} + +bool Gen2GameReader::getEventFlag(uint16_t flagNumber) +{ + const uint16_t saveOffset = (isGameCrystal()) ? EVENT_FLAGS_OFFSET_CRYSTAL : EVENT_FLAGS_OFFSET_GOLDSILVER; + uint8_t byteVal; + const uint8_t flag = 1 << (flagNumber % 8); + + saveManager_.seek(saveOffset + (flagNumber / 8)); + saveManager_.readByte(byteVal); + + return (byteVal & flag); +} + +#include + +void Gen2GameReader::setEventFlag(uint16_t flagNumber, bool enabled) +{ + const uint16_t saveOffset = (isGameCrystal()) ? EVENT_FLAGS_OFFSET_CRYSTAL : EVENT_FLAGS_OFFSET_GOLDSILVER; + uint8_t byteVal; + uint8_t resultVal; + const uint8_t flag = 1 << (flagNumber % 8); + + saveManager_.seek(saveOffset + (flagNumber / 8)); + saveManager_.readByte(byteVal); + saveManager_.rewind(1); + + // first calculate the value of the byte with the bit reset to 0 + resultVal = byteVal & (~flag); + + // now apply the flag if enabled == true + if(enabled) + { + resultVal |= flag; + } + printf("%s: flagNumber %hu, enabled: %d, orig byte: 0x%02x, new byte 0x%02x\n", __FUNCTION__, flagNumber, enabled, byteVal, resultVal); + + saveManager_.writeByte(resultVal); } \ No newline at end of file