mirror of
https://github.com/risingPhil/libpokemegb.git
synced 2026-04-26 02:02:32 -05:00
Add support for gen I localizations
There currently is no way to automatically detect the localization yet though.
This commit is contained in:
parent
2ba33f26f7
commit
3065163b1b
|
|
@ -15,6 +15,7 @@ all:
|
|||
$(MAKE) -C gen2_getEventFlag $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||
$(MAKE) -C gen2_setEventFlag $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||
$(MAKE) -C decodePartyIcons $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||
$(MAKE) -C findLocalizationOffsets $(MAKECMDGOALS) $(MAKEOVERRIDES)
|
||||
|
||||
clean:
|
||||
$(MAKE) -C do_stuff_gen1 clean
|
||||
|
|
@ -30,3 +31,5 @@ clean:
|
|||
$(MAKE) -C gen2_getEventFlag clean
|
||||
$(MAKE) -C gen2_setEventFlag clean
|
||||
$(MAKE) -C decodePartyIcons clean
|
||||
$(MAKE) -C findLocalizationOffsets clean
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@
|
|||
|
||||
using OutputFormat = SpriteRenderer::OutputFormat;
|
||||
|
||||
static const LocalizationLanguage g_localization = LocalizationLanguage::ENGLISH;
|
||||
|
||||
static void decodeGen1Icon(IRomReader& romReader, ISaveManager& saveManager, Gen1GameType gen1Type, Gen1PokemonIconType iconType, bool firstFrame)
|
||||
{
|
||||
Gen1GameReader gameReader(romReader, saveManager, gen1Type);
|
||||
Gen1GameReader gameReader(romReader, saveManager, gen1Type, g_localization);
|
||||
SpriteRenderer renderer;
|
||||
char fileNameBuf[100];
|
||||
uint8_t* spriteBuffer;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
static const LocalizationLanguage g_localization = LocalizationLanguage::ENGLISH;
|
||||
|
||||
static void print_hex(const unsigned char* buffer, size_t length)
|
||||
{
|
||||
for (size_t i = 0; i < length; i++)
|
||||
|
|
@ -22,6 +24,6 @@ int main(int argc, char** argv)
|
|||
|
||||
uint8_t outputBuffer[4096];
|
||||
|
||||
const uint16_t size = gen1_encodePokeText(argv[1], strlen(argv[1]), outputBuffer, sizeof(outputBuffer), 0x50);
|
||||
const uint16_t size = gen1_encodePokeText(argv[1], strlen(argv[1]), outputBuffer, sizeof(outputBuffer), 0x50, g_localization);
|
||||
print_hex(outputBuffer, size);
|
||||
}
|
||||
48
examples/findLocalizationOffsets/Makefile
Normal file
48
examples/findLocalizationOffsets/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 := ../../findLocalizationOffsets
|
||||
|
||||
# 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)
|
||||
142
examples/findLocalizationOffsets/main.cpp
Normal file
142
examples/findLocalizationOffsets/main.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#include "utils.h"
|
||||
#include "RomReader.h"
|
||||
#include "SaveManager.h"
|
||||
#include "gen1/Gen1Common.h"
|
||||
#include "gen1/Gen1Localization.h"
|
||||
#include "gen2/Gen2Common.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
static const LocalizationLanguage g_localization = LocalizationLanguage::ENGLISH;
|
||||
|
||||
static void findBinaryPattern(uint8_t* buffer, size_t bufferSize, uint8_t* pattern, size_t patternSize)
|
||||
{
|
||||
uint8_t* cur = buffer;
|
||||
while(cur)
|
||||
{
|
||||
cur = (uint8_t*)memmem(cur, bufferSize - (cur - buffer), pattern, patternSize);
|
||||
if(cur)
|
||||
{
|
||||
printf("%s: found pattern at offset 0x%08x\n", __FUNCTION__, (uint32_t)(cur - buffer));
|
||||
cur += patternSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t findNames(uint8_t* buffer, size_t bufferSize, const char** nameList, uint8_t nameListSize)
|
||||
{
|
||||
uint8_t nameBuffer[0xB];
|
||||
uint8_t i;
|
||||
uint16_t nameLength;
|
||||
const uint8_t nameEntrySize = 0xA;
|
||||
uint8_t* firstEntry;
|
||||
uint8_t* cur = buffer;
|
||||
bool match;
|
||||
|
||||
nameLength = gen1_encodePokeText(nameList[0], strlen(nameList[0]), nameBuffer, sizeof(nameBuffer), 0x50, g_localization);
|
||||
firstEntry = (uint8_t*)memmem(cur, bufferSize - (cur - buffer), nameBuffer, nameLength);
|
||||
|
||||
while(firstEntry)
|
||||
{
|
||||
cur = firstEntry + nameEntrySize;
|
||||
match = true;
|
||||
for(i = 1; i < nameListSize; ++i)
|
||||
{
|
||||
nameLength = gen1_encodePokeText(nameList[i], strlen(nameList[i]), nameBuffer, sizeof(nameBuffer), 0x50, g_localization);
|
||||
if(memcmp(cur, nameBuffer, nameLength))
|
||||
{
|
||||
//not equal, so no match at this offset
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
cur += nameEntrySize;
|
||||
}
|
||||
|
||||
if(match)
|
||||
{
|
||||
return (uint32_t)(firstEntry - buffer);
|
||||
}
|
||||
// re-encode first name
|
||||
nameLength = gen1_encodePokeText(nameList[0], strlen(nameList[0]), nameBuffer, sizeof(nameBuffer), 0x50, g_localization);
|
||||
// continue searching for another occurrence of the first name starting from the bytes directly after
|
||||
// the previously found location
|
||||
cur = firstEntry + nameEntrySize;
|
||||
firstEntry = (uint8_t*)memmem(cur, bufferSize - (cur - buffer), nameBuffer, nameLength);
|
||||
}
|
||||
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: findLocalizationOffsets <path/to/english_rom.gbc> <path/to/localized_rom.gbc>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t* romBuffer;
|
||||
uint8_t* localizedRomBuffer;
|
||||
uint8_t* saveBuffer = nullptr;
|
||||
uint32_t romFileSize;
|
||||
uint32_t localizedRomBufferSize;
|
||||
uint32_t saveFileSize = 0;
|
||||
|
||||
romBuffer = readFileIntoBuffer(argv[1], romFileSize);
|
||||
if(!romBuffer)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Couldn't read file %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
localizedRomBuffer = readFileIntoBuffer(argv[2], localizedRomBufferSize);
|
||||
if(!localizedRomBuffer)
|
||||
{
|
||||
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(gen2Type != Gen2GameType::INVALID)
|
||||
{
|
||||
fprintf(stderr, "ERROR: sorry, this tool only supports gen 1!\n");
|
||||
return 1;
|
||||
}
|
||||
else if(gen1Type == Gen1GameType::INVALID)
|
||||
{
|
||||
fprintf(stderr, "ERROR: this is not a Gen 2 pokémon game!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// When searching for Red/Blue offsets, use this line.
|
||||
const Gen1LocalizationRomOffsets* eng = g1_localizationOffsetsRB;
|
||||
// For yellow, use this one instead:
|
||||
// const Gen1LocalizationRomOffsets* eng = g1_localizationOffsetsY;
|
||||
printf("Stats:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->stats, 11);
|
||||
printf("Stats Mew:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->statsMew, 11);
|
||||
printf("Numbers:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->numbers, 11);
|
||||
const char* pokeNames[] = {"サイドン"};
|
||||
printf("Names: 0x%08x\n", findNames(localizedRomBuffer, localizedRomBufferSize, pokeNames, 1));
|
||||
printf("IconTypes:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->iconTypes, 11);
|
||||
printf("Icons:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->icons, 6);
|
||||
printf("PaletteIndices:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->paletteIndices, 11);
|
||||
printf("Palettes:\n");
|
||||
findBinaryPattern(localizedRomBuffer, localizedRomBufferSize, romBuffer + eng->palettes, 11);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define _GEN1COMMON_H
|
||||
|
||||
#include "common.h"
|
||||
#include "Gen1Localization.h"
|
||||
|
||||
class Gen1GameReader;
|
||||
typedef struct Gen1DistributionPokemon Gen1DistributionPokemon;
|
||||
|
|
@ -129,11 +130,11 @@ void gen1_recalculatePokeStats(Gen1GameReader& gameReader, Gen1TrainerPokemon& p
|
|||
* @brief This function decodes a text (This could be a name or something else) found in the rom.
|
||||
* @return the number of characters copied to the output buffer
|
||||
*/
|
||||
uint16_t gen1_decodePokeText(const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength);
|
||||
uint16_t gen1_decodePokeText(const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength, LocalizationLanguage language);
|
||||
/**
|
||||
* @brief The opposite of gen1_decodePokeText()
|
||||
*/
|
||||
uint16_t gen1_encodePokeText(const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator);
|
||||
uint16_t gen1_encodePokeText(const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator, LocalizationLanguage language);
|
||||
|
||||
/**
|
||||
* Prepares the gen 1 distribution pokemon for injection with Gen1GameReader::addPokemon()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "gen1/Gen1IconDecoder.h"
|
||||
#include "gen1/Gen1PlayerPokemonStorage.h"
|
||||
#include "gen1/Gen1DistributionPokemon.h"
|
||||
#include "gen1/Gen1Localization.h"
|
||||
|
||||
class IRomReader;
|
||||
class ISaveManager;
|
||||
|
|
@ -12,7 +13,7 @@ class ISaveManager;
|
|||
class Gen1GameReader
|
||||
{
|
||||
public:
|
||||
Gen1GameReader(IRomReader& romReader, ISaveManager& saveManager, Gen1GameType gameType);
|
||||
Gen1GameReader(IRomReader& romReader, ISaveManager& saveManager, Gen1GameType gameType, LocalizationLanguage language = LocalizationLanguage::ENGLISH);
|
||||
|
||||
/**
|
||||
* @brief get the name of a pokémon based on an index number
|
||||
|
|
@ -186,6 +187,7 @@ private:
|
|||
Gen1SpriteDecoder spriteDecoder_;
|
||||
Gen1IconDecoder iconDecoder_;
|
||||
Gen1GameType gameType_;
|
||||
uint8_t localization_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define _GEN1POKEMONICONDECODER_H
|
||||
|
||||
#include "gen1/Gen1Common.h"
|
||||
#include "gen1/Gen1Localization.h"
|
||||
|
||||
#define GEN1_TILE_BITS_PER_PIXEL 2
|
||||
#define GEN1_BYTES_PER_TILE 16
|
||||
|
|
@ -17,7 +18,7 @@ class IRomReader;
|
|||
class Gen1IconDecoder
|
||||
{
|
||||
public:
|
||||
Gen1IconDecoder(IRomReader& romReader, Gen1GameType gameType);
|
||||
Gen1IconDecoder(IRomReader& romReader, Gen1GameType gameType, LocalizationLanguage language);
|
||||
|
||||
/**
|
||||
* @brief Decodes the specified icon type. either frame 1 or frame 2.
|
||||
|
|
@ -33,6 +34,7 @@ private:
|
|||
uint8_t buffer_[GEN1_ICON_NUM_BYTES];
|
||||
IRomReader& romReader_;
|
||||
Gen1GameType gameType_;
|
||||
uint8_t localization_;
|
||||
};
|
||||
|
||||
#endif
|
||||
79
include/gen1/Gen1Localization.h
Normal file
79
include/gen1/Gen1Localization.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef _GEN1LOCALIZATION_H
|
||||
#define _GEN1LOCALIZATION_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief This enum defines the supported localization languages for gen 1.
|
||||
* MAX is invalid. It is defined to be used in a for loop if needed.
|
||||
* It can also be used as an invalid value to force Gen1GameReader to identify
|
||||
* the localization language
|
||||
*/
|
||||
enum class LocalizationLanguage
|
||||
{
|
||||
ENGLISH,
|
||||
FRENCH,
|
||||
SPANISH,
|
||||
GERMAN,
|
||||
ITALIAN,
|
||||
JAPANESE,
|
||||
MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Different pokémon localizations unfortunately also store data at different locations in the rom.
|
||||
* This is because stuff shifts around because of the different text.
|
||||
*
|
||||
* So to deal with different localizations of the same game, we need to keep track of the various rom offsets we need
|
||||
* for each of them.
|
||||
*
|
||||
*/
|
||||
typedef struct Gen1LocalizationRomOffsets
|
||||
{
|
||||
/**
|
||||
* @brief The rom offset at which the species structs can be found containing the base stats
|
||||
* of the various pokémon. https://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_species_data_structure_(Generation_I)
|
||||
*/
|
||||
uint32_t stats;
|
||||
/**
|
||||
* @brief For some reason, in the Pokémon Blue and Red versions, the mew species(stats) struct is stored separately.
|
||||
* This does not apply to Pokémon Yellow
|
||||
*/
|
||||
uint32_t statsMew;
|
||||
/**
|
||||
* @brief This is the rom offset at which the pokedex numbers of pokémon are stored in pokemon index order
|
||||
*/
|
||||
uint32_t numbers;
|
||||
/**
|
||||
* @brief This is the rom offset at which we can find the encoded pokémon names.
|
||||
* These can be decoded with gen1_decodePokeText()
|
||||
*/
|
||||
uint32_t names;
|
||||
|
||||
/**
|
||||
* @brief This is the rom offset at which we can find the party menu icon type for each pokémon in pokedex number order.
|
||||
*/
|
||||
uint32_t iconTypes;
|
||||
/**
|
||||
* @brief This is the rom offset at which we can find the MonPartySpritePointers pointer table
|
||||
* This defines where the icon sprites are stored in the rom (and the number of tiles)
|
||||
*
|
||||
* see https://github.com/pret/pokered/blob/master/data/icon_pointers.asm
|
||||
*/
|
||||
uint32_t icons;
|
||||
|
||||
/**
|
||||
* @brief This is the rom offset at which a palette index is stored for each pokémon in pokedex number order.
|
||||
*/
|
||||
uint32_t paletteIndices;
|
||||
|
||||
/**
|
||||
* @brief This is the rom offset at which each pokémon sprite color palette is stored in palette index order
|
||||
*/
|
||||
uint32_t palettes;
|
||||
} Gen1Localization;
|
||||
|
||||
extern const Gen1LocalizationRomOffsets g1_localizationOffsetsRB[];
|
||||
extern const Gen1LocalizationRomOffsets g1_localizationOffsetsY[];
|
||||
|
||||
#endif
|
||||
|
|
@ -14,7 +14,7 @@ uint8_t getGen1BoxBankIndex(uint8_t boxIndex, uint8_t currentBoxIndex);
|
|||
class Gen1Party
|
||||
{
|
||||
public:
|
||||
Gen1Party(Gen1GameReader& gameReader, ISaveManager& saveManager);
|
||||
Gen1Party(Gen1GameReader& gameReader, ISaveManager& saveManager, LocalizationLanguage language);
|
||||
~Gen1Party();
|
||||
|
||||
/**
|
||||
|
|
@ -41,6 +41,7 @@ protected:
|
|||
private:
|
||||
Gen1GameReader& gameReader_;
|
||||
ISaveManager& saveManager_;
|
||||
LocalizationLanguage localization_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -49,7 +50,7 @@ private:
|
|||
class Gen1Box
|
||||
{
|
||||
public:
|
||||
Gen1Box(Gen1GameReader& gameReader, ISaveManager& saveManager, uint8_t boxIndex);
|
||||
Gen1Box(Gen1GameReader& gameReader, ISaveManager& saveManager, uint8_t boxIndex, LocalizationLanguage language);
|
||||
~Gen1Box();
|
||||
|
||||
/**
|
||||
|
|
@ -73,6 +74,7 @@ private:
|
|||
Gen1GameReader& gameReader_;
|
||||
ISaveManager& saveManager_;
|
||||
uint8_t boxIndex_;
|
||||
LocalizationLanguage localization_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -15,12 +15,12 @@ const uint8_t* memSearch(const uint8_t* haystack, uint32_t hayStackLength, const
|
|||
* @brief This function decodes a text (This could be a name or something else) found in the rom based on the textCodes list you pass in.
|
||||
* @return the number of characters copied to the output buffer
|
||||
*/
|
||||
uint16_t decodeText(struct TextCodePair* textCodes, uint16_t numTextCodes, const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength);
|
||||
uint16_t decodeText(const struct TextCodePair* textCodes, uint16_t numTextCodes, const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength);
|
||||
|
||||
/**
|
||||
* @brief The opposite of decodeText()
|
||||
*/
|
||||
uint16_t encodeText(struct TextCodePair* textCodes, uint16_t numTextCodes, const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator);
|
||||
uint16_t encodeText(const struct TextCodePair* textCodes, uint16_t numTextCodes, const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator);
|
||||
|
||||
bool isCurrentCPULittleEndian();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#define POKEMON_YELLOW_CARTRIDGE_TITLE "POKEMON YELLOW"
|
||||
|
||||
|
||||
static TextCodePair gen1TextCodes[] = {
|
||||
static const TextCodePair gen1TextCodesMain[] = {
|
||||
{0x4F, " "},
|
||||
{0x57, "#"},
|
||||
{0x51, "*"},
|
||||
|
|
@ -116,6 +116,247 @@ static TextCodePair gen1TextCodes[] = {
|
|||
{0xFF, "9"}
|
||||
};
|
||||
|
||||
static const TextCodePair gen1TextCodesJpn[] = {
|
||||
{0x01, "イ゙"},
|
||||
{0x02, "ヴ"},
|
||||
{0x03, "エ゙"},
|
||||
{0x04, "オ゙"},
|
||||
{0x05, "ガ"},
|
||||
{0x06, "ギ"},
|
||||
{0x07, "グ"},
|
||||
{0x08, "ゲ"},
|
||||
{0x09, "ゴ"},
|
||||
{0x0A, "ザ"},
|
||||
{0x0B, "ジ"},
|
||||
{0x0C, "ズ"},
|
||||
{0x0D, "ゼ"},
|
||||
{0x0E, "ゾ"},
|
||||
{0x0F, "ダ"},
|
||||
{0x10, "ヂ"},
|
||||
{0x11, "ヅ"},
|
||||
{0x12, "デ"},
|
||||
{0x13, "ド"},
|
||||
{0x14, "ナ゙"},
|
||||
{0x15, "ニ゙"},
|
||||
{0x16, "ヌ゙"},
|
||||
{0x17, "ネ゙"},
|
||||
{0x18, "ノ゙"},
|
||||
{0x19, "バ"},
|
||||
{0x1A, "ビ"},
|
||||
{0x1B, "ブ"},
|
||||
{0x1C, "ボ"},
|
||||
{0x1D, "マ゙"},
|
||||
{0x1E, "ミ゙"},
|
||||
{0x1F, "ム゙"},
|
||||
{0x20, "ィ゙"},
|
||||
{0x21, "あ゙"},
|
||||
{0x22, "い゙"},
|
||||
{0x23, "ゔ"},
|
||||
{0x24, "え゙"},
|
||||
{0x25, "お゙"},
|
||||
{0x26, "が"},
|
||||
{0x27, "ぎ"},
|
||||
{0x28, "ぐ"},
|
||||
{0x29, "げ"},
|
||||
{0x2A, "ご"},
|
||||
{0x2B, "ざ"},
|
||||
{0x2C, "じ"},
|
||||
{0x2D, "ず"},
|
||||
{0x2E, "ぜ"},
|
||||
{0x2F, "ぞ"},
|
||||
{0x30, "だ"},
|
||||
{0x31, "ぢ"},
|
||||
{0x32, "づ"},
|
||||
{0x33, "で"},
|
||||
{0x34, "ど"},
|
||||
{0x35, "な゙"},
|
||||
{0x36, "に゙"},
|
||||
{0x37, "ぬ゙"},
|
||||
{0x38, "ね゙"},
|
||||
{0x39, "の゙"},
|
||||
{0x3A, "ば"},
|
||||
{0x3B, "び"},
|
||||
{0x3C, "ぶ"},
|
||||
{0x3D, "べ"},
|
||||
{0x3E, "ぼ"},
|
||||
{0x3F, "ま゙"},
|
||||
{0x40, "パ"},
|
||||
{0x41, "ピ"},
|
||||
{0x42, "プ"},
|
||||
{0x43, "ポ"},
|
||||
{0x44, "ぱ"},
|
||||
{0x45, "ぴ"},
|
||||
{0x46, "ぷ"},
|
||||
{0x47, "ぺ"},
|
||||
{0x48, "ぽ"},
|
||||
{0x49, "ま゚"},
|
||||
{0x4D, "も゚"},
|
||||
{0x4F, " "},
|
||||
{0x57, "#"},
|
||||
{0x51, "*"},
|
||||
{0x52, "A1"},
|
||||
{0x53, "A2"},
|
||||
{0x54, "POKé"},
|
||||
{0x55, "+"},
|
||||
{0x58, "$"},
|
||||
{0x5D, "TRAINER"},
|
||||
{0x75, "…"},
|
||||
{0x7F, " "},
|
||||
{0x60, "A"},
|
||||
{0x61, "B"},
|
||||
{0x62, "C"},
|
||||
{0x63, "D"},
|
||||
{0x64, "E"},
|
||||
{0x65, "F"},
|
||||
{0x66, "G"},
|
||||
{0x67, "H"},
|
||||
{0x68, "I"},
|
||||
{0x69, "V"},
|
||||
{0x6A, "S"},
|
||||
{0x6B, "L"},
|
||||
{0x6C, "M"},
|
||||
{0x6D, ":"},
|
||||
{0x6E, "ぃ"},
|
||||
{0x6F, "ぅ"},
|
||||
{0x70, "「"},
|
||||
{0x71, "」"},
|
||||
{0x72, "『"},
|
||||
{0x73, "』"},
|
||||
{0x74, "・"},
|
||||
{0x75, "…"},
|
||||
{0x76, "ぁ"},
|
||||
{0x77, "ぇ"},
|
||||
{0x78, "ぉ"},
|
||||
{0x80, "ア"},
|
||||
{0x81, "イ"},
|
||||
{0x82, "ウ"},
|
||||
{0x83, "エ"},
|
||||
{0x84, "オ"},
|
||||
{0x85, "カ"},
|
||||
{0x86, "キ"},
|
||||
{0x87, "ク"},
|
||||
{0x88, "ケ"},
|
||||
{0x89, "コ"},
|
||||
{0x8A, "サ"},
|
||||
{0x8B, "シ"},
|
||||
{0x8C, "ス"},
|
||||
{0x8D, "セ"},
|
||||
{0x8E, "ソ"},
|
||||
{0x8F, "タ"},
|
||||
{0x90, "チ"},
|
||||
{0x91, "ツ"},
|
||||
{0x92, "テ"},
|
||||
{0x93, "ト"},
|
||||
{0x94, "ナ"},
|
||||
{0x95, "ニ"},
|
||||
{0x96, "ヌ"},
|
||||
{0x97, "ネ"},
|
||||
{0x98, "ノ"},
|
||||
{0x99, "ハ"},
|
||||
{0x9A, "ヒ"},
|
||||
{0x9B, "フ"},
|
||||
{0x9C, "ホ"},
|
||||
{0x9D, "マ"},
|
||||
{0x9E, "ミ"},
|
||||
{0x9F, "ム"},
|
||||
{0xA0, "メ"},
|
||||
{0xA1, "モ"},
|
||||
{0xA2, "ヤ"},
|
||||
{0xA3, "ユ"},
|
||||
{0xA4, "ヨ"},
|
||||
{0xA5, "ラ"},
|
||||
{0xA6, "ル"},
|
||||
{0xA7, "レ"},
|
||||
{0xA8, "ロ"},
|
||||
{0xA9, "ワ"},
|
||||
{0xAA, "ヲ"},
|
||||
{0xAB, "ン"},
|
||||
{0xAC, "ッ"},
|
||||
{0xAD, "ャ"},
|
||||
{0xAE, "ュ"},
|
||||
{0xAF, "ョ"},
|
||||
{0xB0, "ィ"},
|
||||
{0xB1, "あ"},
|
||||
{0xB2, "い"},
|
||||
{0xB3, "う"},
|
||||
{0xB4, "え"},
|
||||
{0xB5, "お"},
|
||||
{0xB6, "か"},
|
||||
{0xB7, "き"},
|
||||
{0xB8, "く"},
|
||||
{0xB9, "け"},
|
||||
{0xBA, "こ"},
|
||||
{0xBB, "さ"},
|
||||
{0xBC, "し"},
|
||||
{0xBD, "す"},
|
||||
{0xBE, "せ"},
|
||||
{0xBF, "そ"},
|
||||
{0xC0, "た"},
|
||||
{0xC1, "ち"},
|
||||
{0xC2, "つ"},
|
||||
{0xC3, "て"},
|
||||
{0xC4, "と"},
|
||||
{0xC5, "な"},
|
||||
{0xC6, "に"},
|
||||
{0xC7, "ぬ"},
|
||||
{0xC8, "ね"},
|
||||
{0xC9, "の"},
|
||||
{0xCA, "は"},
|
||||
{0xCB, "ひ"},
|
||||
{0xCC, "ふ"},
|
||||
{0xCD, "へ"},
|
||||
{0xCE, "ほ"},
|
||||
{0xCF, "ま"},
|
||||
{0xD0, "み"},
|
||||
{0xD1, "む"},
|
||||
{0xD2, "め"},
|
||||
{0xD3, "も"},
|
||||
{0xD4, "や"},
|
||||
{0xD5, "ゆ"},
|
||||
{0xD6, "よ"},
|
||||
{0xD7, "ら"},
|
||||
{0xD8, "リ"},
|
||||
{0xD9, "る"},
|
||||
{0xDA, "れ"},
|
||||
{0xDB, "ろ"},
|
||||
{0xDC, "わ"},
|
||||
{0xDD, "を"},
|
||||
{0xDE, "ん"},
|
||||
{0xDF, "っ"},
|
||||
{0xE0, "ゃ"},
|
||||
{0xE1, "ゅ"},
|
||||
{0xE2, "ょ"},
|
||||
{0xE3, "ー"},
|
||||
{0xE4, "゜"},
|
||||
{0xE5, "゛"},
|
||||
{0xE6, "?"},
|
||||
{0xE7, "!"},
|
||||
{0xE8, "。"},
|
||||
{0xE9, "ァ"},
|
||||
{0xEA, "ゥ"},
|
||||
{0xEB, "ェ"},
|
||||
{0xEC, "▷"},
|
||||
{0xED, "▶"},
|
||||
{0xEE, "▼"},
|
||||
{0xEF, "♂"},
|
||||
{0xF0, "円"},
|
||||
{0xF1, "×"},
|
||||
{0xF2, "."},
|
||||
{0xF3, "/"},
|
||||
{0xF4, "ォ"},
|
||||
{0xF5, "♀"},
|
||||
{0xF6, "0"},
|
||||
{0xF7, "1"},
|
||||
{0xF8, "2"},
|
||||
{0xF9, "3"},
|
||||
{0xFA, "4"},
|
||||
{0xFB, "5"},
|
||||
{0xFC, "6"},
|
||||
{0xFD, "7"},
|
||||
{0xFE, "8"},
|
||||
{0xFF, "9"}
|
||||
};
|
||||
|
||||
Gen1GameType gen1_determineGameType(const GameboyCartridgeHeader& cartridgeHeader)
|
||||
{
|
||||
Gen1GameType result;
|
||||
|
|
@ -151,16 +392,42 @@ void gen1_recalculatePokeStats(Gen1GameReader& reader, Gen1TrainerPokemon& poke)
|
|||
poke.special = calculatePokeStat(PokeStat::SPECIAL, stats.base_special, getStatIV(PokeStat::SPECIAL, poke.iv_data), poke.special_effort_value, poke.level);
|
||||
}
|
||||
|
||||
uint16_t gen1_decodePokeText(const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength)
|
||||
uint16_t gen1_decodePokeText(const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength, LocalizationLanguage language)
|
||||
{
|
||||
const uint16_t numEntries = sizeof(gen1TextCodes) / sizeof(struct TextCodePair);
|
||||
return decodeText(gen1TextCodes, numEntries, inputBuffer, inputBufferLength, outputBuffer, outputBufferLength);
|
||||
const TextCodePair* textCodes;
|
||||
uint16_t numEntries;
|
||||
|
||||
if(language != LocalizationLanguage::JAPANESE)
|
||||
{
|
||||
textCodes = gen1TextCodesMain;
|
||||
numEntries = sizeof(gen1TextCodesMain) / sizeof(struct TextCodePair);
|
||||
}
|
||||
else
|
||||
{
|
||||
textCodes = gen1TextCodesJpn;
|
||||
numEntries = sizeof(gen1TextCodesJpn) / sizeof(struct TextCodePair);
|
||||
}
|
||||
|
||||
return decodeText(textCodes, numEntries, inputBuffer, inputBufferLength, outputBuffer, outputBufferLength);
|
||||
}
|
||||
|
||||
uint16_t gen1_encodePokeText(const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator)
|
||||
uint16_t gen1_encodePokeText(const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator, LocalizationLanguage language)
|
||||
{
|
||||
const uint16_t numEntries = sizeof(gen1TextCodes) / sizeof(struct TextCodePair);
|
||||
return encodeText(gen1TextCodes, numEntries, inputBuffer, inputBufferLength, outputBuffer, outputBufferLength, terminator);
|
||||
const TextCodePair* textCodes;
|
||||
uint16_t numEntries;
|
||||
|
||||
if(language != LocalizationLanguage::JAPANESE)
|
||||
{
|
||||
textCodes = gen1TextCodesMain;
|
||||
numEntries = sizeof(gen1TextCodesMain) / sizeof(struct TextCodePair);
|
||||
}
|
||||
else
|
||||
{
|
||||
textCodes = gen1TextCodesJpn;
|
||||
numEntries = sizeof(gen1TextCodesJpn) / sizeof(struct TextCodePair);
|
||||
}
|
||||
|
||||
return encodeText(textCodes, numEntries, inputBuffer, inputBufferLength, outputBuffer, outputBufferLength, terminator);
|
||||
}
|
||||
|
||||
void gen1_prepareDistributionPokemon(Gen1GameReader& gameReader, const Gen1DistributionPokemon& distributionPoke, Gen1TrainerPokemon& poke, const char*& originalTrainerName)
|
||||
|
|
|
|||
|
|
@ -47,12 +47,13 @@ uint8_t calculateWholeBoxBankChecksum(ISaveManager& saveManager, uint8_t bankInd
|
|||
return checksum.get();
|
||||
}
|
||||
|
||||
Gen1GameReader::Gen1GameReader(IRomReader &romReader, ISaveManager &saveManager, Gen1GameType gameType)
|
||||
Gen1GameReader::Gen1GameReader(IRomReader &romReader, ISaveManager &saveManager, Gen1GameType gameType, LocalizationLanguage language)
|
||||
: romReader_(romReader)
|
||||
, saveManager_(saveManager)
|
||||
, spriteDecoder_(romReader_)
|
||||
, iconDecoder_(romReader, gameType)
|
||||
, iconDecoder_(romReader, gameType, language)
|
||||
, gameType_(gameType)
|
||||
, localization_((uint8_t)language)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -62,23 +63,15 @@ const char *Gen1GameReader::getPokemonName(uint8_t index) const
|
|||
static char result[20];
|
||||
uint8_t encodedText[0xA];
|
||||
uint32_t numRead;
|
||||
uint16_t pointer;
|
||||
|
||||
if(gameType_ == Gen1GameType::BLUE || gameType_ == Gen1GameType::RED)
|
||||
{
|
||||
romReader_.seek(0x2FA3);
|
||||
const uint8_t bankByte = romReader_.peek();
|
||||
|
||||
romReader_.seek(0x2FAE);
|
||||
romReader_.readUint16(pointer);
|
||||
|
||||
// seek to the right location
|
||||
romReader_.seekToRomPointer(pointer, bankByte);
|
||||
romReader_.seek(g1_localizationOffsetsRB[localization_].names);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pkmn Yellow
|
||||
romReader_.seek(0xE8000);
|
||||
romReader_.seek(g1_localizationOffsetsY[localization_].names);
|
||||
}
|
||||
|
||||
romReader_.advance((index - 1) * 0xA);
|
||||
|
|
@ -86,7 +79,7 @@ const char *Gen1GameReader::getPokemonName(uint8_t index) const
|
|||
// max 10 bytes
|
||||
numRead = romReader_.readUntil(encodedText, 0x50, 0xA);
|
||||
|
||||
gen1_decodePokeText(encodedText, numRead, result, sizeof(result));
|
||||
gen1_decodePokeText(encodedText, numRead, result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +87,7 @@ uint8_t Gen1GameReader::getPokemonNumber(uint8_t index) const
|
|||
{
|
||||
// Based on https://github.com/seanmorris/pokemon-parser/blob/master/source/PokemonRom.js#L509
|
||||
uint8_t result = 0xFF;
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? 0x410B1 : 0x41024;
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? g1_localizationOffsetsY[localization_].numbers : g1_localizationOffsetsRB[localization_].numbers;
|
||||
romReader_.seek(romOffset + (index - 1));
|
||||
romReader_.readByte(result);
|
||||
return result;
|
||||
|
|
@ -105,7 +98,7 @@ Gen1PokemonIconType Gen1GameReader::getPokemonIconType(uint8_t index) const
|
|||
//MonPartyData in pret/pokered and pret/pokeyellow
|
||||
// strangely, this array is in pokemon _number_ order, not index
|
||||
uint8_t number = getPokemonNumber(index);
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? 0x719BA : 0x7190D;
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? g1_localizationOffsetsY[localization_].iconTypes : g1_localizationOffsetsRB[localization_].iconTypes;
|
||||
uint8_t byteVal;
|
||||
Gen1PokemonIconType result;
|
||||
|
||||
|
|
@ -198,8 +191,6 @@ bool Gen1GameReader::readPokemonStatsForIndex(uint8_t index, Gen1PokeStats &outS
|
|||
const uint8_t pokeNumber = getPokemonNumber(index);
|
||||
|
||||
uint8_t spriteBank;
|
||||
uint8_t statsBank;
|
||||
uint16_t statsPointer;
|
||||
|
||||
if (gameType_ != Gen1GameType::YELLOW && index == 0x15)
|
||||
{
|
||||
|
|
@ -235,23 +226,14 @@ bool Gen1GameReader::readPokemonStatsForIndex(uint8_t index, Gen1PokeStats &outS
|
|||
// Mew (of which the pokenumber is 151) stats are stored at a completely different location in the rom than the rest
|
||||
if (pokeNumber != 151)
|
||||
{
|
||||
romReader_.seek(0x153B);
|
||||
romReader_.readByte(statsBank);
|
||||
|
||||
romReader_.seek(0x1578);
|
||||
romReader_.readUint16(statsPointer);
|
||||
romReader_.seek(g1_localizationOffsetsRB[localization_].stats);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mew stats
|
||||
romReader_.seek(0x159C);
|
||||
romReader_.readByte(statsBank);
|
||||
|
||||
romReader_.seek(0x1593);
|
||||
romReader_.readUint16(statsPointer);
|
||||
romReader_.seek(g1_localizationOffsetsRB[localization_].statsMew);
|
||||
}
|
||||
|
||||
romReader_.seekToRomPointer(statsPointer, statsBank);
|
||||
if (pokeNumber != 151)
|
||||
{
|
||||
// the number is 1-based.
|
||||
|
|
@ -261,7 +243,7 @@ bool Gen1GameReader::readPokemonStatsForIndex(uint8_t index, Gen1PokeStats &outS
|
|||
else
|
||||
{
|
||||
// Dealing with Pokemon yellow
|
||||
romReader_.seek(0x383DE);
|
||||
romReader_.seek(g1_localizationOffsetsY[localization_].stats);
|
||||
// the number is 1-based.
|
||||
romReader_.advance(statsStructSize * (pokeNumber - 1));
|
||||
}
|
||||
|
|
@ -290,7 +272,7 @@ uint8_t Gen1GameReader::getColorPaletteIndexByPokemonNumber(uint8_t pokeNumber)
|
|||
{
|
||||
uint8_t result;
|
||||
// pokeyellow.map from https://github.com/pret/pokeyellow (after compilation)
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? 0x72922 : 0x725C9;
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? g1_localizationOffsetsY[localization_].paletteIndices : g1_localizationOffsetsRB[localization_].paletteIndices;
|
||||
if(!romReader_.seek(romOffset + (pokeNumber - 1)))
|
||||
{
|
||||
return 0xFF;
|
||||
|
|
@ -311,7 +293,7 @@ void Gen1GameReader::readColorPalette(uint8_t paletteId, uint16_t* outColorPalet
|
|||
|
||||
// based on https://datacrystal.romhacking.net/wiki/Pok%C3%A9mon_Red_and_Blue/ROM_map
|
||||
// and https://bulbapedia.bulbagarden.net/wiki/List_of_color_palettes_by_index_number_(Generation_I)
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? 0x72AF9 : 0x72660;
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? g1_localizationOffsetsY[localization_].palettes : g1_localizationOffsetsRB[localization_].palettes;
|
||||
romReader_.seek(romOffset + (paletteId * 8));
|
||||
while(cur < end)
|
||||
{
|
||||
|
|
@ -328,7 +310,7 @@ const char *Gen1GameReader::getTrainerName() const
|
|||
saveManager_.seek(0x2598);
|
||||
|
||||
saveManager_.readUntil(encodedPlayerName, 0x50, 0xB);
|
||||
gen1_decodePokeText(encodedPlayerName, sizeof(encodedPlayerName), result, sizeof(result));
|
||||
gen1_decodePokeText(encodedPlayerName, sizeof(encodedPlayerName), result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +321,7 @@ const char *Gen1GameReader::getRivalName() const
|
|||
saveManager_.seek(0x25F6);
|
||||
|
||||
saveManager_.readUntil(encodedRivalName, 0x50, sizeof(encodedRivalName));
|
||||
gen1_decodePokeText(encodedRivalName, sizeof(encodedRivalName), result, sizeof(result));
|
||||
gen1_decodePokeText(encodedRivalName, sizeof(encodedRivalName), result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -355,12 +337,12 @@ uint16_t Gen1GameReader::getTrainerID() const
|
|||
|
||||
Gen1Party Gen1GameReader::getParty()
|
||||
{
|
||||
return Gen1Party((*this), saveManager_);
|
||||
return Gen1Party((*this), saveManager_, (LocalizationLanguage)localization_);
|
||||
}
|
||||
|
||||
Gen1Box Gen1GameReader::getBox(uint8_t boxIndex)
|
||||
{
|
||||
return Gen1Box((*this), saveManager_, boxIndex);
|
||||
return Gen1Box((*this), saveManager_, boxIndex, (LocalizationLanguage)localization_);
|
||||
}
|
||||
|
||||
uint8_t Gen1GameReader::getCurrentBoxIndex()
|
||||
|
|
|
|||
|
|
@ -20,10 +20,11 @@ static uint8_t reverseByte(uint8_t incoming)
|
|||
return result;
|
||||
}
|
||||
|
||||
Gen1IconDecoder::Gen1IconDecoder(IRomReader& romReader, Gen1GameType gameType)
|
||||
Gen1IconDecoder::Gen1IconDecoder(IRomReader& romReader, Gen1GameType gameType, LocalizationLanguage language)
|
||||
: buffer_()
|
||||
, romReader_(romReader)
|
||||
, gameType_(gameType)
|
||||
, localization_((uint8_t)language)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -43,7 +44,7 @@ Gen1IconDecoder::Gen1IconDecoder(IRomReader& romReader, Gen1GameType gameType)
|
|||
*/
|
||||
uint8_t* Gen1IconDecoder::decode(Gen1PokemonIconType iconType, bool firstFrame)
|
||||
{
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? 0x7184D : 0x717C0;
|
||||
const uint32_t romOffset = (gameType_ == Gen1GameType::YELLOW) ? g1_localizationOffsetsY[localization_].icons : g1_localizationOffsetsRB[localization_].icons;
|
||||
const uint32_t ENTRY_SIZE = 6;
|
||||
const uint8_t MAX_NUM_TILES = 8;
|
||||
|
||||
|
|
|
|||
139
src/gen1/Gen1Localization.cpp
Normal file
139
src/gen1/Gen1Localization.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "gen1/Gen1Localization.h"
|
||||
|
||||
const Gen1LocalizationRomOffsets g1_localizationOffsetsRB[] = {
|
||||
// English
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x425B,
|
||||
.numbers = 0x41024,
|
||||
.names = 0x1c21e,
|
||||
.iconTypes = 0x7190D,
|
||||
.icons = 0x717C0,
|
||||
.paletteIndices = 0x725C9,
|
||||
.palettes = 0x72660
|
||||
},
|
||||
// French
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x425B,
|
||||
.numbers = 0x40FAA,
|
||||
.names = 0x1C21E,
|
||||
.iconTypes = 0x718DE,
|
||||
.icons = 0x71791,
|
||||
.paletteIndices = 0x7259A,
|
||||
.palettes = 0x72631
|
||||
},
|
||||
// Spanish
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x425B,
|
||||
.numbers = 0x40FB4,
|
||||
.names = 0x1C21E,
|
||||
.iconTypes = 0x718FD,
|
||||
.icons = 0x717B0,
|
||||
.paletteIndices = 0x725B9,
|
||||
.palettes = 0x72650
|
||||
},
|
||||
// German
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x425B,
|
||||
.numbers = 0x40F96,
|
||||
.names = 0x1C21E,
|
||||
.iconTypes = 0x718E7,
|
||||
.icons = 0x7179A,
|
||||
.paletteIndices = 0x725A3,
|
||||
.palettes = 0x7263A
|
||||
},
|
||||
// Italian
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x425B,
|
||||
.numbers = 0x40FB6,
|
||||
.names = 0x1C21E,
|
||||
.iconTypes = 0x7194D,
|
||||
.icons = 0x71800,
|
||||
.paletteIndices = 0x72609,
|
||||
.palettes = 0x726A0
|
||||
},
|
||||
// Japanese
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x425B,
|
||||
.numbers = 0x42784,
|
||||
.names = 0x39446,
|
||||
.iconTypes = 0x71DC1,
|
||||
.icons = 0x71C74,
|
||||
.paletteIndices = 0x72A0E,
|
||||
.palettes = 0x72AA5
|
||||
}
|
||||
};
|
||||
|
||||
const Gen1LocalizationRomOffsets g1_localizationOffsetsY[] = {
|
||||
// English
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x0, // IRRELEVANT because mew is included in the stats list for Yellow
|
||||
.numbers = 0x410B1,
|
||||
.names = 0xE8000,
|
||||
.iconTypes = 0x719BA,
|
||||
.icons = 0x7184D,
|
||||
.paletteIndices = 0x72922,
|
||||
.palettes = 0x72AF9
|
||||
},
|
||||
// French
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x0, // IRRELEVANT because mew is included in the stats list for Yellow
|
||||
.numbers = 0x41036,
|
||||
.names = 0xE8000,
|
||||
.iconTypes = 0x7198B,
|
||||
.icons = 0x7181E,
|
||||
.paletteIndices = 0x728F3,
|
||||
.palettes = 0x72ACA
|
||||
},
|
||||
// Spanish
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x0, // IRRELEVANT because mew is included in the stats list for Yellow
|
||||
.numbers = 0x41041,
|
||||
.names = 0xE8000,
|
||||
.iconTypes = 0x719AA,
|
||||
.icons = 0x7183D,
|
||||
.paletteIndices = 0x72912,
|
||||
.palettes = 0x72AE9
|
||||
},
|
||||
// German
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x0, // IRRELEVANT because mew is included in the stats list for Yellow
|
||||
.numbers = 0x41023,
|
||||
.names = 0xE8000,
|
||||
.iconTypes = 0x71999,
|
||||
.icons = 0x7182c,
|
||||
.paletteIndices = 0x72901,
|
||||
.palettes = 0x72AD8
|
||||
},
|
||||
// Italian
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x0, // IRRELEVANT because mew is included in the stats list for Yellow
|
||||
.numbers = 0x41043,
|
||||
.names = 0xE8000,
|
||||
.iconTypes = 0x719FA,
|
||||
.icons = 0x7188D,
|
||||
.paletteIndices = 0x72962,
|
||||
.palettes = 0x72B39
|
||||
},
|
||||
// Japanese
|
||||
{
|
||||
.stats = 0x383DE,
|
||||
.statsMew = 0x0, // IRRELEVANT because mew is included in the stats list for Yellow
|
||||
.numbers = 0x4282D,
|
||||
.names = 0x39462,
|
||||
.iconTypes = 0x71911,
|
||||
.icons = 0x717A4,
|
||||
.paletteIndices = 0x72650,
|
||||
.palettes = 0x726E7
|
||||
}
|
||||
};
|
||||
|
|
@ -188,9 +188,10 @@ static uint8_t calculateBoxChecksum(ISaveManager& saveManager, uint8_t boxIndex,
|
|||
return checksum.get();
|
||||
}
|
||||
|
||||
Gen1Party::Gen1Party(Gen1GameReader& gameReader, ISaveManager& savManager)
|
||||
Gen1Party::Gen1Party(Gen1GameReader& gameReader, ISaveManager& savManager, LocalizationLanguage language)
|
||||
: gameReader_(gameReader)
|
||||
, saveManager_(savManager)
|
||||
, localization_(language)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +311,7 @@ const char* Gen1Party::getPokemonNickname(uint8_t partyIndex)
|
|||
saveManager_.seek(0x2F2C + FIRST_NICKNAME_NAME_OFFSET + (partyIndex * NICKNAME_SIZE));
|
||||
saveManager_.readUntil(encodedNickName, 0x50, NICKNAME_SIZE);
|
||||
|
||||
gen1_decodePokeText(encodedNickName, sizeof(encodedNickName), result, sizeof(result));
|
||||
gen1_decodePokeText(encodedNickName, sizeof(encodedNickName), result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -326,7 +327,7 @@ void Gen1Party::setPokemonNickname(uint8_t partyIndex, const char* name)
|
|||
name = gameReader_.getPokemonName(poke.poke_index);
|
||||
}
|
||||
|
||||
const uint16_t encodedLength = gen1_encodePokeText(name, strlen(name), encodedNickName, NICKNAME_SIZE, 0x50);
|
||||
const uint16_t encodedLength = gen1_encodePokeText(name, strlen(name), encodedNickName, NICKNAME_SIZE, 0x50, (LocalizationLanguage)localization_);
|
||||
saveManager_.seek(0x2F2C + FIRST_NICKNAME_NAME_OFFSET + (partyIndex * NICKNAME_SIZE));
|
||||
saveManager_.write(encodedNickName, encodedLength);
|
||||
}
|
||||
|
|
@ -340,7 +341,7 @@ const char* Gen1Party::getOriginalTrainerOfPokemon(uint8_t partyIndex)
|
|||
saveManager_.seek(0x2F2C + FIRST_OT_NAME_OFFSET + (partyIndex * OT_NAME_SIZE));
|
||||
saveManager_.readUntil(encodedOTName, 0x50, OT_NAME_SIZE);
|
||||
|
||||
gen1_decodePokeText(encodedOTName, sizeof(encodedOTName), result, sizeof(result));
|
||||
gen1_decodePokeText(encodedOTName, sizeof(encodedOTName), result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -350,7 +351,7 @@ void Gen1Party::setOriginalTrainerOfPokemon(uint8_t partyIndex, const char* orig
|
|||
uint8_t encodedOTName[OT_NAME_SIZE];
|
||||
const uint16_t FIRST_OT_NAME_OFFSET = 0x110;
|
||||
|
||||
const uint16_t encodedLength = gen1_encodePokeText(originalTrainer, strlen(originalTrainer), encodedOTName, OT_NAME_SIZE, 0x50);
|
||||
const uint16_t encodedLength = gen1_encodePokeText(originalTrainer, strlen(originalTrainer), encodedOTName, OT_NAME_SIZE, 0x50, (LocalizationLanguage)localization_);
|
||||
|
||||
saveManager_.seek(0x2F2C + FIRST_OT_NAME_OFFSET + (partyIndex * OT_NAME_SIZE));
|
||||
saveManager_.write(encodedOTName, encodedLength);
|
||||
|
|
@ -388,10 +389,11 @@ bool Gen1Party::add(Gen1TrainerPokemon& poke, const char* originalTrainerID, con
|
|||
return true;
|
||||
}
|
||||
|
||||
Gen1Box::Gen1Box(Gen1GameReader& gameReader, ISaveManager& saveManager, uint8_t boxIndex)
|
||||
Gen1Box::Gen1Box(Gen1GameReader& gameReader, ISaveManager& saveManager, uint8_t boxIndex, LocalizationLanguage language)
|
||||
: gameReader_(gameReader)
|
||||
, saveManager_(saveManager)
|
||||
, boxIndex_(boxIndex)
|
||||
, localization_(language)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +507,7 @@ const char* Gen1Box::getPokemonNickname(uint8_t index)
|
|||
saveManager_.seekToBankOffset(bankIndex, boxOffset + nicknameOffset + (index * NICKNAME_SIZE));
|
||||
saveManager_.readUntil(encodedNickName, 0x50, NICKNAME_SIZE);
|
||||
|
||||
gen1_decodePokeText(encodedNickName, sizeof(encodedNickName), result, sizeof(result));
|
||||
gen1_decodePokeText(encodedNickName, sizeof(encodedNickName), result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -524,7 +526,7 @@ void Gen1Box::setPokemonNickname(uint8_t index, const char* name)
|
|||
name = gameReader_.getPokemonName(poke.poke_index);
|
||||
}
|
||||
|
||||
const uint16_t encodedLength = gen1_encodePokeText(name, strlen(name), encodedNickName, NICKNAME_SIZE, 0x50);
|
||||
const uint16_t encodedLength = gen1_encodePokeText(name, strlen(name), encodedNickName, NICKNAME_SIZE, 0x50, (LocalizationLanguage)localization_);
|
||||
saveManager_.seekToBankOffset(bankIndex, boxOffset + nicknameOffset + (index * NICKNAME_SIZE));
|
||||
saveManager_.write(encodedNickName, encodedLength);
|
||||
}
|
||||
|
|
@ -542,7 +544,7 @@ const char* Gen1Box::getOriginalTrainerOfPokemon(uint8_t index)
|
|||
saveManager_.seekToBankOffset(bankIndex, boxOffset + otOffset + (index * OT_NAME_SIZE));
|
||||
saveManager_.readUntil(encodedOTName, 0x50, OT_NAME_SIZE);
|
||||
|
||||
gen1_decodePokeText(encodedOTName, sizeof(encodedOTName), result, sizeof(result));
|
||||
gen1_decodePokeText(encodedOTName, sizeof(encodedOTName), result, sizeof(result), (LocalizationLanguage)localization_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -556,7 +558,7 @@ void Gen1Box::setOriginalTrainerOfPokemon(uint8_t index, const char* originalTra
|
|||
const uint16_t boxOffset = getBoxBankOffset(boxIndex_, currentBoxIndex);
|
||||
|
||||
|
||||
const uint16_t encodedLength = gen1_encodePokeText(originalTrainer, strlen(originalTrainer), encodedOTName, OT_NAME_SIZE, 0x50);
|
||||
const uint16_t encodedLength = gen1_encodePokeText(originalTrainer, strlen(originalTrainer), encodedOTName, OT_NAME_SIZE, 0x50, (LocalizationLanguage)localization_);
|
||||
|
||||
saveManager_.seekToBankOffset(bankIndex, boxOffset + otOffset + (index * OT_NAME_SIZE));
|
||||
saveManager_.write(encodedOTName, encodedLength);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include <png.h>
|
||||
#endif
|
||||
|
||||
static const char* findCharsByTextCode(TextCodePair* textCodes, uint16_t numEntries, uint8_t code)
|
||||
static const char* findCharsByTextCode(const TextCodePair* textCodes, uint16_t numEntries, uint8_t code)
|
||||
{
|
||||
const TextCodePair* const end = textCodes + numEntries;
|
||||
const TextCodePair* cur = textCodes;
|
||||
|
|
@ -27,7 +27,7 @@ static const char* findCharsByTextCode(TextCodePair* textCodes, uint16_t numEntr
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static void findTextcodeByString(TextCodePair* textCodes, uint16_t numEntries, const char* input, uint16_t inputLength, uint8_t& outCode, uint16_t& needleLength)
|
||||
static void findTextcodeByString(const TextCodePair* textCodes, uint16_t numEntries, const char* input, uint16_t inputLength, uint8_t& outCode, uint16_t& needleLength)
|
||||
{
|
||||
const TextCodePair* const end = textCodes + numEntries;
|
||||
const TextCodePair* cur = textCodes;
|
||||
|
|
@ -79,7 +79,7 @@ const uint8_t* memSearch(const uint8_t* haystack, uint32_t haystackLength, const
|
|||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t decodeText(struct TextCodePair* textCodes, uint16_t numTextCodes, const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength)
|
||||
uint16_t decodeText(const struct TextCodePair* textCodes, uint16_t numTextCodes, const uint8_t* inputBuffer, uint16_t inputBufferLength, char* outputBuffer, uint16_t outputBufferLength)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
if(inputBufferLength > outputBufferLength)
|
||||
|
|
@ -124,7 +124,7 @@ uint16_t decodeText(struct TextCodePair* textCodes, uint16_t numTextCodes, const
|
|||
return result;
|
||||
}
|
||||
|
||||
uint16_t encodeText(struct TextCodePair* textCodes, uint16_t numTextCodes, const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator)
|
||||
uint16_t encodeText(const struct TextCodePair* textCodes, uint16_t numTextCodes, const char* inputBuffer, uint16_t inputBufferLength, uint8_t* outputBuffer, uint16_t outputBufferLength, uint8_t terminator)
|
||||
{
|
||||
uint16_t needleLength;
|
||||
uint8_t code;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user