Fix bug gen2 addItem

This commit is contained in:
risingPhil 2024-07-19 21:31:16 +02:00
parent 20ba1f67fe
commit 747bdd4b4b
4 changed files with 205 additions and 10 deletions

View File

@ -8,6 +8,7 @@ all:
$(MAKE) -C encodeText_gen2 $(MAKECMDGOALS) $(MAKEOVERRIDES)
$(MAKE) -C decodeSprite $(MAKECMDGOALS) $(MAKEOVERRIDES)
$(MAKE) -C addDistributionPoke $(MAKECMDGOALS) $(MAKEOVERRIDES)
$(MAKE) -C gen2_gsball $(MAKECMDGOALS) $(MAKEOVERRIDES)
clean:
$(MAKE) -C do_stuff_gen1 clean
@ -16,3 +17,4 @@ clean:
$(MAKE) -C encodeText_gen2 clean
$(MAKE) -C decodeSprite clean
$(MAKE) -C addDistributionPoke clean
$(MAKE) -C gen2_gsball clean

View File

@ -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_gsball
# 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)

View File

@ -0,0 +1,116 @@
#include "gen1/Gen1GameReader.h"
#include "gen1/Gen1DistributionPokemon.h"
#include "gen2/Gen2GameReader.h"
#include "gen2/Gen2DistributionPokemon.h"
#include "RomReader.h"
#include "SaveManager.h"
#include "SpriteRenderer.h"
#include "utils.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define POKEMON_CRYSTAL_ITEM_ID_GS_BALL 0x73
static void gen2ReceiveGSBall(Gen2GameReader& gameReader)
{
bool alreadyHasOne = false;
const char* trainerName = gameReader.getTrainerName();
Gen2ItemList keyItemPocket = gameReader.getItemList(Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET);
if(keyItemPocket.getCount() > 0)
{
uint8_t itemId;
uint8_t itemCount;
bool gotEntry = keyItemPocket.getEntry(0, itemId, itemCount);
while(gotEntry)
{
if(itemId == POKEMON_CRYSTAL_ITEM_ID_GS_BALL)
{
alreadyHasOne = true;
break;
}
gotEntry = keyItemPocket.getNextEntry(itemId, itemCount);
}
}
if(alreadyHasOne)
{
fprintf(stderr, "ERROR: It appears you already have one!");
}
else
{
keyItemPocket.add(POKEMON_CRYSTAL_ITEM_ID_GS_BALL, 1);
gameReader.finishSave();
fprintf(stdout, "%s obtained a GS Ball!", trainerName);
}
}
int main(int argc, char** argv)
{
if(argc != 3)
{
fprintf(stderr, "Usage: addDistributionPoke <path/to/rom.gbc> <path/to/file.sav>\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 2
const Gen2GameType gen2Type = gen2_determineGameType(cartridgeHeader);
if(gen2Type == Gen2GameType::INVALID)
{
fprintf(stderr, "ERROR: not a gen 2 game!\n");
return 1;
}
if(gen2Type != Gen2GameType::CRYSTAL)
{
fprintf(stderr, "ERROR: not Pokémon Crystal!\n");
// return 1;
}
Gen2GameReader gameReader(romReader, saveManager, gen2Type);
gen2ReceiveGSBall(gameReader);
// Test for adding rare candy
#if 0
Gen2ItemList itemPocket = gameReader.getItemList(Gen2ItemListType::GEN2_ITEMLISTTYPE_ITEMPOCKET);
itemPocket.add(0x20, 27);
gameReader.finishSave();
#endif
FILE* f = fopen(argv[2], "w");
fwrite(saveBuffer, 1, saveFileSize, f);
fclose(f);
free(romBuffer);
free(saveBuffer);
romBuffer = 0;
saveBuffer = 0;
return 0;
}

View File

@ -155,7 +155,7 @@ uint8_t Gen2ItemList::getCount()
uint8_t Gen2ItemList::getCapacity()
{
return 20;
return (type_ == Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET) ? 26 : 20;
}
bool Gen2ItemList::getEntry(uint8_t index, uint8_t &outItemId, uint8_t &outCount)
@ -165,7 +165,11 @@ bool Gen2ItemList::getEntry(uint8_t index, uint8_t &outItemId, uint8_t &outCount
return false;
}
if (!saveManager_.advance(1 + (index * 2)))
// It looks like the key item pocket doesn't have a count value.
// During my test if I try to write a GS ball with count 1 to crystal and store the count value, I get a random master ball showing up (which has itemId 1)
// before the gs ball in the list.
const uint32_t advanceAmount = (type_ == Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET) ? 1 + index : 1 + (index * 2);
if (!saveManager_.advance(advanceAmount))
{
return false;
}
@ -183,8 +187,20 @@ bool Gen2ItemList::getNextEntry(uint8_t &outItemId, uint8_t &outCount)
return false;
}
saveManager_.readByte(outCount);
// 2 things here:
// - The bulbapedia article: https://bulbapedia.bulbagarden.net/wiki/Save_data_structure_(Generation_II)#Item_lists
// gets the order of count vs itemId wrong. At least during my tests with a Pokémon crystal AND gold save
// - The key item pocket doesn't appear to store a count value at all.
// see my other comment at ::getEntry() for more info
saveManager_.readByte(outItemId);
if(type_ != Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET)
{
saveManager_.readByte(outCount);
}
else
{
outCount = 1;
}
return true;
}
@ -203,13 +219,19 @@ bool Gen2ItemList::add(uint8_t itemId, uint8_t itemCount)
{
if(curItemId == itemId)
{
uint8_t newCount = curItemCount + itemCount;
if(newCount > 99)
// the key item pocket doesn't appear to have a count field per entry.
// For more info see the related comment @getEntry()
if(type_ != Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET)
{
newCount = 99;
uint8_t newCount = curItemCount + itemCount;
if(newCount > 99)
{
newCount = 99;
}
saveManager_.rewind(1); // count is the second field of each entry (yes, Bulbapedia appears to get it wrong here)
saveManager_.writeByte(newCount);
saveManager_.advance();
}
saveManager_.rewind();
saveManager_.writeByte(newCount);
return true;
}
}
@ -221,12 +243,19 @@ bool Gen2ItemList::add(uint8_t itemId, uint8_t itemCount)
return false;
}
// Bulbapedia gets the order wrong here. itemId should go first, then itemCount
// at least this is the case during my tests with a Pokémon Crystal AND Gold save
saveManager_.writeByte(itemId);
// in the while loop, getNextEntry has returned false on the last iteration, so that means
// we've arrived at the terminator of the list. This is the exact position at which we need to write our new entry.
// convenient, isn't it?
// so let's write the new entry...
saveManager_.writeByte(itemCount);
saveManager_.writeByte(itemId);
if(type_ != Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET)
{
// the key item pocket doesn't appear to have a count field per entry.
// For more info see the related comment @getEntry()
saveManager_.writeByte(itemCount);
}
// now write the new terminator
saveManager_.writeByte(0xFF);