mirror of
https://github.com/risingPhil/libpokemegb.git
synced 2026-04-26 10:16:36 -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 encodeText_gen2 $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||||
$(MAKE) -C decodeSprite $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
$(MAKE) -C decodeSprite $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||||
$(MAKE) -C addDistributionPoke $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
$(MAKE) -C addDistributionPoke $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||||
|
$(MAKE) -C gen2_gsball $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(MAKE) -C do_stuff_gen1 clean
|
$(MAKE) -C do_stuff_gen1 clean
|
||||||
|
|
@ -16,3 +17,4 @@ clean:
|
||||||
$(MAKE) -C encodeText_gen2 clean
|
$(MAKE) -C encodeText_gen2 clean
|
||||||
$(MAKE) -C decodeSprite clean
|
$(MAKE) -C decodeSprite clean
|
||||||
$(MAKE) -C addDistributionPoke 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()
|
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)
|
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;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -183,8 +187,20 @@ bool Gen2ItemList::getNextEntry(uint8_t &outItemId, uint8_t &outCount)
|
||||||
return false;
|
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);
|
saveManager_.readByte(outItemId);
|
||||||
|
if(type_ != Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET)
|
||||||
|
{
|
||||||
|
saveManager_.readByte(outCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -203,13 +219,19 @@ bool Gen2ItemList::add(uint8_t itemId, uint8_t itemCount)
|
||||||
{
|
{
|
||||||
if(curItemId == itemId)
|
if(curItemId == itemId)
|
||||||
{
|
{
|
||||||
uint8_t newCount = curItemCount + itemCount;
|
// the key item pocket doesn't appear to have a count field per entry.
|
||||||
if(newCount > 99)
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,12 +243,19 @@ bool Gen2ItemList::add(uint8_t itemId, uint8_t itemCount)
|
||||||
return false;
|
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
|
// 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.
|
// 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?
|
// convenient, isn't it?
|
||||||
// so let's write the new entry...
|
// so let's write the new entry...
|
||||||
saveManager_.writeByte(itemCount);
|
if(type_ != Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET)
|
||||||
saveManager_.writeByte(itemId);
|
{
|
||||||
|
// 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
|
// now write the new terminator
|
||||||
saveManager_.writeByte(0xFF);
|
saveManager_.writeByte(0xFF);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user