mirror of
https://github.com/risingPhil/libpokemegb.git
synced 2026-03-21 17:44:24 -05:00
Fix bug gen2 addItem
This commit is contained in:
parent
20ba1f67fe
commit
747bdd4b4b
|
|
@ -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
|
||||
|
|
|
|||
48
examples/gen2_gsball/Makefile
Normal file
48
examples/gen2_gsball/Makefile
Normal 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)
|
||||
116
examples/gen2_gsball/main.cpp
Normal file
116
examples/gen2_gsball/main.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user