mirror of
https://github.com/GearsProgress/Poke_Transporter_GB.git
synced 2026-03-21 17:34:42 -05:00
Implement zx0 compression
Compress data tables with the ZX0 compression algorithm
This commit is contained in:
parent
ba8738fc4b
commit
532a095d77
12
Dockerfile
Normal file
12
Dockerfile
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
FROM devkitpro/devkitarm
|
||||
|
||||
LABEL author="Poke Transporter GB"
|
||||
|
||||
USER root
|
||||
|
||||
ARG USER_ID
|
||||
ARG GROUP_ID
|
||||
|
||||
ENV DEBIAN_FRONTEND="noninteractive"
|
||||
|
||||
RUN apt update && apt install -y build-essential
|
||||
425
Makefile
425
Makefile
|
|
@ -1,204 +1,221 @@
|
|||
# Build configuration (set to either 'debug' or 'release')
|
||||
BUILD_TYPE := debug
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/gba_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# the LIBGBA path is defined in gba_rules, but we have to define LIBTONC ourselves
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBTONC := $(DEVKITPRO)/libtonc
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary data
|
||||
# GRAPHICS is a list of directories containing files to be processed by grit
|
||||
#
|
||||
# All directories are specified relative to the project directory where
|
||||
# the makefile is found
|
||||
#
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))_mb
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
INCLUDES := include
|
||||
DATA :=
|
||||
MUSIC := audio
|
||||
GRAPHICS := graphics
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -mthumb -mthumb-interwork
|
||||
|
||||
CFLAGS := -g -Wall -O2\
|
||||
-mcpu=arm7tdmi -mtune=arm7tdmi -masm-syntax-unified\
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -ffunction-sections -fdata-sections -Os -Wall -mthumb -mcpu=arm7tdmi -mtune=arm7tdmi
|
||||
CXXFLAGS := $(CFLAGS) -g0 -fno-rtti -fno-exceptions -fdata-sections -ffunction-sections -std=c++20 -Wno-volatile -D_GLIBCXX_USE_CXX20_ABI=0
|
||||
|
||||
ifeq ($(BUILD_TYPE), debug)
|
||||
CFLAGS += -g -DDEBUG
|
||||
CXXFLAGS += -g -DDEBUG
|
||||
else ifeq ($(BUILD_TYPE), release)
|
||||
|
||||
|
||||
endif
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -Os -g $(ARCH) -Wl,-Map,$(notdir $*.map) -Wl,--gc-sections -mthumb -mcpu=arm7tdmi -mtune=arm7tdmi -Wl,-Map,output.map,--cref -nodefaultlibs
|
||||
|
||||
CFLAGS += -flto
|
||||
LDFLAGS += -flto
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lmm -ltonc -lgba -lc -lgcc -lsysbase
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib.
|
||||
# the LIBGBA path should remain in this list if you want to use maxmod
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBGBA) $(LIBTONC)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#ifneq ($(strip $(MUSIC)),)
|
||||
# export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir))
|
||||
# BINFILES += soundbank.bin
|
||||
#endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
|
||||
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export OFILES_GRAPHICS := $(PNGFILES:.png=.o)
|
||||
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) $(OFILES_GRAPHICS)
|
||||
|
||||
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(PNGFILES:.png=.h)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
@mkdir -p loader/data
|
||||
@cp $(TARGET).gba loader/data/multiboot_rom.bin
|
||||
@$(MAKE) -C loader
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@$(MAKE) -C loader clean
|
||||
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
$(OUTPUT).gba : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SOURCES) : $(HFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# The bin2o rule should be copied and modified
|
||||
# for each extension used in the data directories
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rule to build soundbank from music files
|
||||
#---------------------------------------------------------------------------------
|
||||
#soundbank.bin soundbank.h : $(AUDIOFILES)
|
||||
#---------------------------------------------------------------------------------
|
||||
# @mmutil $^ -osoundbank.bin -hsoundbank.h
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule links in binary data with the .bin extension
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule creates C source files using grit
|
||||
# grit takes an image file and a .grit describing how the file is to be processed
|
||||
# add additional rules like this for each image extension
|
||||
# you use in the graphics folders
|
||||
#---------------------------------------------------------------------------------
|
||||
%.c %.h: %.png %.grit
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo "grit $(notdir $<)"
|
||||
@grit $< -ftc -o$*
|
||||
|
||||
# make likes to delete intermediate files. This prevents it from deleting the
|
||||
# files generated by grit after building the GBA ROM.
|
||||
.SECONDARY:
|
||||
|
||||
-include $(DEPSDIR)/*.d
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
# Build configuration (set to either 'debug' or 'release')
|
||||
BUILD_TYPE := release
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/gba_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# the LIBGBA path is defined in gba_rules, but we have to define LIBTONC ourselves
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBTONC := $(DEVKITPRO)/libtonc
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary data
|
||||
# GRAPHICS is a list of directories containing files to be processed by grit
|
||||
#
|
||||
# All directories are specified relative to the project directory where
|
||||
# the makefile is found
|
||||
#
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))_mb
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
INCLUDES := include
|
||||
DATA := data
|
||||
MUSIC := audio
|
||||
GRAPHICS := graphics
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -mthumb -mthumb-interwork
|
||||
|
||||
CFLAGS := -Wall -O2\
|
||||
-mcpu=arm7tdmi -mtune=arm7tdmi -masm-syntax-unified\
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -ffunction-sections -fdata-sections -Os -Wall -mthumb -mcpu=arm7tdmi -mtune=arm7tdmi
|
||||
CXXFLAGS := $(CFLAGS) -g0 -fno-rtti -fno-exceptions -fdata-sections -ffunction-sections -std=c++20 -Wno-volatile -D_GLIBCXX_USE_CXX20_ABI=0
|
||||
|
||||
ifeq ($(BUILD_TYPE), debug)
|
||||
CFLAGS += -g -DDEBUG
|
||||
CXXFLAGS += -g -DDEBUG
|
||||
else ifeq ($(BUILD_TYPE), release)
|
||||
|
||||
|
||||
endif
|
||||
|
||||
ASFLAGS := $(ARCH)
|
||||
LDFLAGS = -Os $(ARCH) -Wl,-Map,$(notdir $*.map) -Wl,--gc-sections -mthumb -mcpu=arm7tdmi -mtune=arm7tdmi -Wl,-Map,output.map,--cref -nodefaultlibs
|
||||
|
||||
CFLAGS += -flto
|
||||
LDFLAGS += -flto
|
||||
|
||||
ifeq ($(BUILD_TYPE), debug)
|
||||
ASFLAGS += -g
|
||||
LDFLAGS += -g
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lmm -ltonc -lgba -lc -lgcc -lsysbase
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib.
|
||||
# the LIBGBA path should remain in this list if you want to use maxmod
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBGBA) $(LIBTONC)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
|
||||
|
||||
#ifneq ($(strip $(MUSIC)),)
|
||||
# export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir))
|
||||
# BINFILES += soundbank.bin
|
||||
#endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export OFILES_GRAPHICS := $(PNGFILES:.png=.o)
|
||||
|
||||
export OFILES := $(OFILES_SOURCES) $(OFILES_GRAPHICS)
|
||||
|
||||
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(PNGFILES:.png=.h)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) generate_data clean
|
||||
|
||||
all: $(BUILD)
|
||||
|
||||
generate_data:
|
||||
mkdir -p data
|
||||
@env -i PATH=$(PATH) $(MAKE) -C tools/compressZX0
|
||||
@env -i PATH=$(PATH) $(MAKE) -C tools/data-generator
|
||||
@cd tools/data-generator && ./data-generator
|
||||
@find tools/data-generator -name *.bin | xargs -i tools/compressZX0/compressZX0 {} data/
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD): generate_data
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
@mkdir -p loader/data
|
||||
@cp $(TARGET).gba loader/data/multiboot_rom.bin
|
||||
@$(MAKE) -C loader
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@$(MAKE) -C tools/compressZX0 clean
|
||||
@$(MAKE) -C tools/data-generator clean
|
||||
@$(MAKE) -C loader clean
|
||||
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba data/
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
BINFILES := $(foreach dir,../$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
OFILES += $(OFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
$(OUTPUT).gba : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SOURCES) : $(HFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# The bin2o rule should be copied and modified
|
||||
# for each extension used in the data directories
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rule to build soundbank from music files
|
||||
#---------------------------------------------------------------------------------
|
||||
#soundbank.bin soundbank.h : $(AUDIOFILES)
|
||||
#---------------------------------------------------------------------------------
|
||||
# @mmutil $^ -osoundbank.bin -hsoundbank.h
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule links in binary data with the .bin extension
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule creates C source files using grit
|
||||
# grit takes an image file and a .grit describing how the file is to be processed
|
||||
# add additional rules like this for each image extension
|
||||
# you use in the graphics folders
|
||||
#---------------------------------------------------------------------------------
|
||||
%.c %.h: %.png %.grit
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo "grit $(notdir $<)"
|
||||
@grit $< -ftc -o$*
|
||||
|
||||
# make likes to delete intermediate files. This prevents it from deleting the
|
||||
# files generated by grit after building the GBA ROM.
|
||||
.SECONDARY:
|
||||
|
||||
-include $(DEPSDIR)/*.d
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
|
|
|
|||
5
buildDockerImg.sh
Executable file
5
buildDockerImg.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
mkdir -p docker-build && cd docker-build
|
||||
docker build -t ptgb-builder:latest -f ../Dockerfile .
|
||||
cd .. && rm -rf docker-build
|
||||
|
||||
|
|
@ -1,27 +1,27 @@
|
|||
# This docker-compose file allows you to pull the devkitpro/devkitarm docker container
|
||||
# and use it to build the Poke Transporter GB rom(s).
|
||||
#
|
||||
# To launch the container itself in the background:
|
||||
# docker compose up -d
|
||||
# Note the container name being printed in the terminal. In my case it was this: poke_transporter_gb-build-1
|
||||
#
|
||||
# Then get a terminal into the container like this:
|
||||
# docker exec -it poke_transporter_gb-build-1 /bin/bash
|
||||
#
|
||||
# Now you can build the project inside this terminal.
|
||||
|
||||
version: "3.5"
|
||||
|
||||
services:
|
||||
build:
|
||||
image: devkitpro/devkitarm
|
||||
working_dir: /usr/Poke_Transporter_GB
|
||||
command: tail -F anything
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ${SSH_AUTH_SOCK}
|
||||
target: ${SSH_AUTH_SOCK}
|
||||
- type: bind
|
||||
source: ${PWD}
|
||||
target: /usr/Poke_Transporter_GB
|
||||
|
||||
# This docker-compose file allows you to pull the devkitpro/devkitarm docker container
|
||||
# and use it to build the Poke Transporter GB rom(s).
|
||||
#
|
||||
# To launch the container itself in the background:
|
||||
# docker compose up -d
|
||||
# Note the container name being printed in the terminal. In my case it was this: poke_transporter_gb-build-1
|
||||
#
|
||||
# Then get a terminal into the container like this:
|
||||
# docker exec -it poke_transporter_gb-build-1 /bin/bash
|
||||
#
|
||||
# Now you can build the project inside this terminal.
|
||||
|
||||
version: "3.5"
|
||||
|
||||
services:
|
||||
build:
|
||||
image: ptgb-builder:latest
|
||||
working_dir: /usr/Poke_Transporter_GB
|
||||
command: tail -F anything
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ${SSH_AUTH_SOCK}
|
||||
target: ${SSH_AUTH_SOCK}
|
||||
- type: bind
|
||||
source: ${PWD}
|
||||
target: /usr/Poke_Transporter_GB
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace ptgb
|
|||
class vector
|
||||
{
|
||||
public:
|
||||
static constexpr size_t default_capacity = 10;
|
||||
static constexpr size_t default_capacity = 4;
|
||||
|
||||
vector()
|
||||
: buffer_()
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@
|
|||
#define CPU_SET_32BIT 0x04000000
|
||||
class mystery_gift_script
|
||||
{
|
||||
PokemonTables &data_tables;
|
||||
int curr_mg_index;
|
||||
int curr_section30_index;
|
||||
u8 mg_script[MG_SCRIPT_SIZE] = {};
|
||||
|
|
@ -264,7 +265,7 @@ class mystery_gift_script
|
|||
u8 four_align_value = 0;
|
||||
|
||||
public:
|
||||
mystery_gift_script();
|
||||
mystery_gift_script(PokemonTables &data_tables);
|
||||
void build_script(Pokemon_Party &incoming_box_data);
|
||||
//void build_script_old(Pokemon_Party &incoming_box_data);
|
||||
u8 get_script_value_at(int index);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#define GEN2_JPN_SIZE 383
|
||||
#define GEN2_INT_SIZE 444
|
||||
|
||||
class PokemonTables;
|
||||
|
||||
struct Simplified_Pokemon
|
||||
{
|
||||
byte dex_number;
|
||||
|
|
@ -37,18 +39,18 @@ public:
|
|||
int unown_letter = -1;
|
||||
Pokemon();
|
||||
void load_data(int index, const byte *party_data, int game, int lang);
|
||||
void convert_to_gen_three(bool simplified, bool stabilize_mythical);
|
||||
void convert_to_gen_three(PokemonTables& data_tables, bool simplified, bool stabilize_mythical);
|
||||
void copy_from_to(const byte *source, byte *destination, int size, bool reverse_endian);
|
||||
void alocate_data_chunks(byte *G, byte *A, byte *E, byte *M);
|
||||
void insert_data(byte *first, byte *second, byte *third, byte *fourth);
|
||||
byte get_gen_3_data(int index);
|
||||
byte *get_full_gen_3_array();
|
||||
byte get_unencrypted_data(int index);
|
||||
byte *convert_text(byte *text_array, int size, int gen, int lang);
|
||||
u32 generate_pid_save_iv(byte pid_species_index, byte nature, byte *pid_dvs);
|
||||
u32 generate_pid_iv_match(byte pid_species_index, byte nature, byte *pid_dvs);
|
||||
byte *convert_text(PokemonTables& data_tables, byte *text_array, int size);
|
||||
u32 generate_pid_save_iv(PokemonTables &data_tables, byte pid_species_index, byte nature, byte *pid_dvs);
|
||||
u32 generate_pid_iv_match(PokemonTables& data_tables, byte pid_species_index, byte nature, byte *pid_dvs);
|
||||
byte rand_reverse_mod(byte modulo_divisor, byte target_mod);
|
||||
byte get_rand_gender_byte(byte index_num, byte attack_DVs);
|
||||
byte get_rand_gender_byte(PokemonTables &data_tables, byte index_num, byte attack_DVs);
|
||||
byte get_dex_number();
|
||||
bool get_validity();
|
||||
bool get_is_new();
|
||||
|
|
@ -57,7 +59,7 @@ public:
|
|||
u8 get_letter_from_pid(u32 pid);
|
||||
u8 get_nature_from_pid(u32 pid);
|
||||
u8 get_gender_from_pid(u32 pid);
|
||||
void set_to_event(byte nature);
|
||||
void set_to_event(PokemonTables &data_tables, byte nature);
|
||||
int num_in_box;
|
||||
int index_in_box;
|
||||
bool is_missingno = false;
|
||||
|
|
|
|||
|
|
@ -39,37 +39,85 @@
|
|||
#define NUM_POKEMON 252
|
||||
#define POKEMON_ARRAY_SIZE NUM_POKEMON + 1
|
||||
|
||||
extern const u8 EXP_GROUPS[POKEMON_ARRAY_SIZE];
|
||||
extern const u32 EXP_MAXIMUMS[6];
|
||||
extern const int GENDER_THRESHOLDS[2][8];
|
||||
extern const u8 GENDER_RATIO[POKEMON_ARRAY_SIZE];
|
||||
extern const bool NUM_ABILITIES[POKEMON_ARRAY_SIZE];
|
||||
extern const byte MOVESETS[POKEMON_ARRAY_SIZE][32];
|
||||
extern const byte FIRST_MOVES[POKEMON_ARRAY_SIZE];
|
||||
extern const char* NAMES[POKEMON_ARRAY_SIZE];
|
||||
extern const u16 JPN_NAMES[POKEMON_ARRAY_SIZE][6];
|
||||
extern const u8 EVOLUTIONS[POKEMON_ARRAY_SIZE];
|
||||
extern const u8 POWER_POINTS[252];
|
||||
extern const u8 MENU_SPRITE_PALS[POKEMON_ARRAY_SIZE + 26][2];
|
||||
extern const byte gen_1_index_array[191];
|
||||
extern const u16 gen_1_Jpn_char_array[256];
|
||||
extern const u16 gen_1_Eng_char_array[256];
|
||||
extern const u16 gen_1_FreGer_char_array[256];
|
||||
extern const u16 gen_1_ItaSpa_char_array[256];
|
||||
extern const u16 gen_2_Jpn_char_array[256];
|
||||
extern const u16 gen_2_Eng_char_array[256];
|
||||
extern const u16 gen_2_FreGer_char_array[256];
|
||||
extern const u16 gen_2_ItaSpa_char_array[256];
|
||||
extern const u16 gen_3_Jpn_char_array[256];
|
||||
extern const u16 gen_3_Intern_char_array[256];
|
||||
extern const byte EVENT_PKMN[8][80];
|
||||
extern const u8 TYPES[POKEMON_ARRAY_SIZE][2];
|
||||
|
||||
u32 get_max_exp(int index_num);
|
||||
u8 get_gender_threshold(int index_num, bool is_gen_3);
|
||||
bool get_num_abilities(int index_num);
|
||||
bool can_learn_move(int pkmn_index, int move_index);
|
||||
byte get_earliest_move(int index_num);
|
||||
byte get_gen_3_char(u16 input_char, bool is_jpn);
|
||||
/**
|
||||
* Okay, here's the thing: to reduce the rom size, we compressed a bunch of data with ZX0
|
||||
* Among this data are various data tables that were previously just stored as const arrays.
|
||||
*
|
||||
* But, during the mystery_gift_builder/mystery_gift_injector execution,
|
||||
* these data tables are used intensely, because you're not using them for single pokémon, but rather for boxes of pokémon.
|
||||
*
|
||||
* Decompression is not cheap, so we can't afford to decompress the tables again and again for every pokémon we deal with
|
||||
* during this flow.
|
||||
*
|
||||
* This is where this class comes in. It is basically a holder of these tables. The idea is to pass it along with the mystery_gift_builder
|
||||
* and have it lazy decompress the tables AND charsets it needs. This way when we are dealing with multiple pokémon, we are not decompressing
|
||||
* the same data every time and thereby make performance suffer.
|
||||
*
|
||||
* It DOES have a significant IWRAM cost though. The intention is to just allocate this struct on the stack.
|
||||
* As far as I can tell, our stack is not really restricted at all. It just fills up the IWRAM as needed. So we need to be careful not to
|
||||
* overwrite/corrupt any code or data we specifically tagged to be stored in IWRAM.
|
||||
*/
|
||||
class PokemonTables
|
||||
{
|
||||
public:
|
||||
bool exp_groups_loaded;
|
||||
bool gender_ratios_loaded;
|
||||
bool num_abilities_loaded;
|
||||
bool first_moves_loaded;
|
||||
bool evolutions_loaded;
|
||||
bool power_points_loaded;
|
||||
bool gen_1_index_array_loaded;
|
||||
bool event_pkmn_loaded;
|
||||
bool types_loaded;
|
||||
// a number representing the unique combination of gen 1/2 and the specific language
|
||||
// 0 means not loaded
|
||||
u8 input_charset_type;
|
||||
// 0 means not loaded, 1=JPN, 2=Intern
|
||||
u8 gen3_charset_type;
|
||||
bool movesets_loaded;
|
||||
|
||||
u8 EXP_GROUPS[POKEMON_ARRAY_SIZE];
|
||||
u8 GENDER_RATIO[POKEMON_ARRAY_SIZE];
|
||||
bool NUM_ABILITIES[POKEMON_ARRAY_SIZE];
|
||||
byte FIRST_MOVES[POKEMON_ARRAY_SIZE];
|
||||
u8 EVOLUTIONS[POKEMON_ARRAY_SIZE];
|
||||
u8 POWER_POINTS[252];
|
||||
byte gen_1_index_array[191];
|
||||
byte EVENT_PKMN[8][80];
|
||||
u8 TYPES[POKEMON_ARRAY_SIZE][2];
|
||||
u16 input_charset[256];
|
||||
u16 gen3_charset[256];
|
||||
byte MOVESETS[POKEMON_ARRAY_SIZE][32];
|
||||
|
||||
PokemonTables();
|
||||
|
||||
void load_exp_groups();
|
||||
void load_gender_ratios();
|
||||
void load_num_abilities();
|
||||
void load_first_moves();
|
||||
void load_evolutions();
|
||||
void load_power_points();
|
||||
void load_gen1_index_array();
|
||||
void load_event_pkmn();
|
||||
void load_types();
|
||||
void load_input_charset(byte gen, byte lang);
|
||||
void load_gen3_charset(byte lang);
|
||||
void load_movesets();
|
||||
|
||||
u32 get_max_exp(int index_num);
|
||||
u8 get_gender_threshold(int index_num, bool is_gen_3);
|
||||
bool get_num_abilities(int index_num);
|
||||
bool can_learn_move(int pkmn_index, int move_index);
|
||||
byte get_earliest_move(int index_num);
|
||||
byte get_gen_3_char(u16 input_char);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the charset for <gen> and <lang> into <output_char_array>
|
||||
*/
|
||||
void load_localized_charset(u16 *output_char_array, byte gen, byte lang);
|
||||
byte get_char_from_charset(const u16 *charset, u16 input_char);
|
||||
#endif
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
void start_link();
|
||||
void continue_link(bool cancel_connection);
|
||||
int get_last_error();
|
||||
Pokemon get_converted_pkmn(int index);
|
||||
Pokemon get_converted_pkmn(PokemonTables &data_tables, int index);
|
||||
bool get_has_new_pkmn();
|
||||
void set_game(int nGame);
|
||||
void set_lang(int nLang);
|
||||
|
|
@ -21,7 +21,7 @@ public:
|
|||
void show_sprites();
|
||||
Simplified_Pokemon simple_pkmn_array[30];
|
||||
Simplified_Pokemon get_simple_pkmn(int index);
|
||||
bool fill_simple_pkmn_array();
|
||||
bool fill_simple_pkmn_array(PokemonTables &data_tables);
|
||||
bool get_contains_mythical();
|
||||
void set_mythic_stabilization(bool stabilize);
|
||||
bool contains_valid = false;
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@ class script_obj
|
|||
{
|
||||
public:
|
||||
script_obj();
|
||||
script_obj(const byte* nText, int nNext); // For dialogue
|
||||
script_obj(int nRun, int nNext); // For commands
|
||||
script_obj(int nRun, int nNext_if_true, int nNext_if_false); // for conditionals
|
||||
script_obj(const byte* nText, uint16_t nNext); // For dialogue
|
||||
script_obj(uint16_t nRun, uint16_t nNext); // For commands
|
||||
script_obj(uint16_t nRun, uint16_t nNext_if_true, uint16_t nNext_if_false); // for conditionals
|
||||
|
||||
const byte* get_text();
|
||||
int get_true_index();
|
||||
int get_false_index();
|
||||
int get_cond_id();
|
||||
uint16_t get_true_index();
|
||||
uint16_t get_false_index();
|
||||
uint16_t get_cond_id();
|
||||
|
||||
private:
|
||||
const byte* text;
|
||||
bool has_text = false;
|
||||
int next_index;
|
||||
int conditional_index;
|
||||
int next_false_index;
|
||||
uint16_t next_index;
|
||||
uint16_t conditional_index;
|
||||
uint16_t next_false_index;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -49,9 +49,9 @@ class textbox_var : public xse_var
|
|||
public:
|
||||
using xse_var::xse_var;
|
||||
void set_text(const byte nText[]);
|
||||
void insert_text(u8 mg_array[]);
|
||||
void insert_text(const u16 *charset, u8 mg_array[]);
|
||||
void set_start();
|
||||
void insert_virtual_text(u8 mg_array[]);
|
||||
void insert_virtual_text(const u16 *charset, u8 mg_array[]);
|
||||
void set_virtual_start();
|
||||
const byte *text;
|
||||
int text_length;
|
||||
|
|
|
|||
|
|
@ -11,21 +11,21 @@
|
|||
class Select_Menu
|
||||
{
|
||||
public:
|
||||
Select_Menu(bool enable_cancel, int nMenu_type, int nStartX, int nStartY);
|
||||
Select_Menu(bool enable_cancel, u8 nMenu_type, int nStartX, int nStartY);
|
||||
int select_menu_main();
|
||||
void hide_menu();
|
||||
void show_menu();
|
||||
void clear_options();
|
||||
void add_option(const byte *option, int return_value);
|
||||
void set_lang(int nLang);
|
||||
void add_option(const byte *option, u8 return_value);
|
||||
void set_lang(u8 nLang);
|
||||
|
||||
private:
|
||||
ptgb::vector<const byte*> menu_options;
|
||||
ptgb::vector<int> return_values;
|
||||
unsigned int curr_selection;
|
||||
ptgb::vector<u8> return_values;
|
||||
u16 curr_selection;
|
||||
bool cancel_enabled;
|
||||
int menu_type;
|
||||
int lang;
|
||||
u8 menu_type;
|
||||
u8 lang;
|
||||
int startTileX;
|
||||
int startTileY;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -125,11 +125,11 @@ void load_textbox_background();
|
|||
void load_flex_background(int background_id, int layer);
|
||||
void load_eternal_sprites();
|
||||
void load_temp_box_sprites(Pokemon_Party *party_data);
|
||||
void load_type_sprites(int pkmn_index, int dex_offset, bool is_caught);
|
||||
void load_type_sprites(const u8* pkmn_type_table, int pkmn_index, int dex_offset, bool is_caught);
|
||||
void add_menu_box(int options, int startTileX, int startTileY);
|
||||
void add_menu_box(int startTileX, int startTileY, int width, int height);
|
||||
void reload_textbox_background();
|
||||
void load_select_sprites(int game_id, int lang);
|
||||
void load_select_sprites(u8 game_id, u8 lang);
|
||||
void fennel_blink(int frame);
|
||||
void fennel_speak(int frame);
|
||||
int get_curr_flex_background();
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ void set_text_exit();
|
|||
int ptgb_write(const char *text);
|
||||
int ptgb_write(const byte *text, bool instant);
|
||||
int ptgb_write(const byte *text, bool instant, int length);
|
||||
int ptgb_write_debug(const char *text, bool instant);
|
||||
int ptgb_write_debug(const u16* charset, const char *text, bool instant);
|
||||
void wait_for_user_to_continue(bool clear_text);
|
||||
|
||||
#endif
|
||||
50
include/zx0_decompressor.h
Normal file
50
include/zx0_decompressor.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef _ZX0_DECODE_H
|
||||
#define _ZX0_DECODE_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// The ZX0 decompressor offers functionality to decompress data
|
||||
// compressed with the ZX0 algorithm (see tools/compressZX0)
|
||||
// This algorithm was invented by Einar Saukas
|
||||
// Original implementation can be found here: https://github.com/einar-saukas/ZX0
|
||||
// However, we've implemented a custom variant of this algorithm.
|
||||
// (for instance: we're storing the uncompressed size in the first 2 bytes in little endian)
|
||||
|
||||
// Our implementation "streams" the decompression: the decompression buffer is only 2 KB, so it can't fit the entire
|
||||
// uncompressed file at once. Therefore it uses a ringbuffer to stream the decompression on-demand.
|
||||
|
||||
extern "C"
|
||||
{
|
||||
/**
|
||||
* @brief This function slots the specified input_data buffer into the zx0 decompressor.
|
||||
* Calling this function effectively resets the ZX0 decompressors' internal state.
|
||||
*/
|
||||
void zx0_decompressor_set_input(const uint8_t *input_data);
|
||||
|
||||
/**
|
||||
* @brief This function returns the uncompressed size of the current input_data buffer.
|
||||
* It reads this from the first 2 bytes of input_data
|
||||
*/
|
||||
uint16_t zx0_decompressor_get_decompressed_size();
|
||||
|
||||
/**
|
||||
* @brief This function seeks to the specified OUTPUT buffer position.
|
||||
* NOTE: this is an expensive operation!
|
||||
* ZX0 doesn't actually support random access.
|
||||
*
|
||||
* So if we're seeking forward, we're actually uncompressing the data until we reach the desired output buffer position.
|
||||
* And if we're seeking backward, we're actually starting to uncompress from scratch until the desired output buffer position!
|
||||
*
|
||||
* So handle this with care!
|
||||
* @param output_byte_pos
|
||||
*/
|
||||
void zx0_decompressor_seek(uint16_t output_byte_pos);
|
||||
|
||||
/**
|
||||
* @brief This function copies <num_bytes> of decompressed data into the specified <output_buffer>
|
||||
* It will trigger decompression on the go (streaming basis)
|
||||
*/
|
||||
uint16_t zx0_decompressor_read(uint8_t *output_buffer, uint16_t num_bytes);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include "libstd_replacements.h"
|
||||
#include "flash_mem.h"
|
||||
#include "pokemon.h"
|
||||
#include "pokemon_data.h"
|
||||
#include "rom_data.h"
|
||||
#include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h"
|
||||
#include "text_engine.h"
|
||||
|
|
@ -96,10 +97,14 @@ void initalize_memory_locations()
|
|||
void print_mem_section()
|
||||
{
|
||||
return; // This function isn't really needed now
|
||||
uint16_t charset[256];
|
||||
byte out[4] = {0, 0, 0, 0xFF};
|
||||
out[0] = get_gen_3_char(mem_name, false);
|
||||
out[1] = get_gen_3_char('-', false);
|
||||
out[2] = get_gen_3_char(mem_id + 0xA1, false); // Kinda a dumb way to
|
||||
|
||||
load_localized_charset(charset, 3, ENG_ID);
|
||||
|
||||
out[0] = get_char_from_charset(charset, mem_name);
|
||||
out[1] = get_char_from_charset(charset, '-');
|
||||
out[2] = get_char_from_charset(charset, mem_id + 0xA1); // Kinda a dumb way to
|
||||
tte_set_pos(0, 0);
|
||||
ptgb_write(out, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "text_engine.h"
|
||||
#include "background_engine.h"
|
||||
#include "pokemon_party.h"
|
||||
#include "pokemon_data.h"
|
||||
#include "script_array.h"
|
||||
#include "sprite_data.h"
|
||||
#include "button_handler.h"
|
||||
|
|
@ -253,6 +254,8 @@ int credits()
|
|||
}
|
||||
if (ENABLE_DEBUG_SCREEN && key_hit(KEY_SELECT))
|
||||
{
|
||||
uint16_t charset[256];
|
||||
load_localized_charset(charset, 3, ENG_ID);
|
||||
if (key_held(KEY_UP) && key_held(KEY_L) && key_held(KEY_R))
|
||||
{
|
||||
set_treecko(true);
|
||||
|
|
@ -270,48 +273,48 @@ int credits()
|
|||
int def_lang = get_def_lang_num();
|
||||
|
||||
create_textbox(4, 1, 160, 80, true);
|
||||
ptgb_write_debug("Debug info:\n\nG: ", true);
|
||||
ptgb_write_debug(ptgb::to_string(curr_rom.language), true);
|
||||
ptgb_write_debug(charset, "Debug info:\n\nG: ", true);
|
||||
ptgb_write_debug(charset, ptgb::to_string(curr_rom.language), true);
|
||||
switch (curr_rom.gamecode)
|
||||
{
|
||||
case RUBY_ID:
|
||||
ptgb_write_debug("-R-", true);
|
||||
ptgb_write_debug(charset, "-R-", true);
|
||||
break;
|
||||
case SAPPHIRE_ID:
|
||||
ptgb_write_debug("-S-", true);
|
||||
ptgb_write_debug(charset, "-S-", true);
|
||||
break;
|
||||
case FIRERED_ID:
|
||||
ptgb_write_debug("-F-", true);
|
||||
ptgb_write_debug(charset, "-F-", true);
|
||||
break;
|
||||
case LEAFGREEN_ID:
|
||||
ptgb_write_debug("-L-", true);
|
||||
ptgb_write_debug(charset, "-L-", true);
|
||||
break;
|
||||
case EMERALD_ID:
|
||||
ptgb_write_debug("-E-", true);
|
||||
ptgb_write_debug(charset, "-E-", true);
|
||||
break;
|
||||
}
|
||||
|
||||
ptgb_write_debug(ptgb::to_string(curr_rom.version), true);
|
||||
ptgb_write_debug(charset, ptgb::to_string(curr_rom.version), true);
|
||||
|
||||
ptgb_write_debug("\nF: ", true);
|
||||
ptgb_write_debug(ptgb::to_string(e4_flag), true);
|
||||
ptgb_write_debug(ptgb::to_string(mg_flag), true);
|
||||
ptgb_write_debug(ptgb::to_string(all_collected_flag), true);
|
||||
ptgb_write_debug("-", true);
|
||||
ptgb_write_debug(charset, "\nF: ", true);
|
||||
ptgb_write_debug(charset, ptgb::to_string(e4_flag), true);
|
||||
ptgb_write_debug(charset, ptgb::to_string(mg_flag), true);
|
||||
ptgb_write_debug(charset, ptgb::to_string(all_collected_flag), true);
|
||||
ptgb_write_debug(charset, "-", true);
|
||||
|
||||
n2hexstr(hexBuffer, pkmn_flags);
|
||||
ptgb_write_debug(hexBuffer, true);
|
||||
ptgb_write_debug("\nS: ", true);
|
||||
ptgb_write_debug(ptgb::to_string(tutorial), true);
|
||||
ptgb_write_debug("-", true);
|
||||
ptgb_write_debug(charset, hexBuffer, true);
|
||||
ptgb_write_debug(charset, "\nS: ", true);
|
||||
ptgb_write_debug(charset, ptgb::to_string(tutorial), true);
|
||||
ptgb_write_debug(charset, "-", true);
|
||||
n2hexstr(hexBuffer, def_lang);
|
||||
ptgb_write_debug(hexBuffer, true);
|
||||
ptgb_write_debug(charset, hexBuffer, true);
|
||||
|
||||
ptgb_write_debug("\n", true);
|
||||
ptgb_write_debug(VERSION, true);
|
||||
ptgb_write_debug(charset, "\n", true);
|
||||
ptgb_write_debug(charset, VERSION, true);
|
||||
if (get_treecko_enabled())
|
||||
{
|
||||
ptgb_write_debug(".T", true);
|
||||
ptgb_write_debug(charset, ".T", true);
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ int var_script_ptr_low = (VAR_ID_START + 0x01);
|
|||
int var_script_ptr_high = (VAR_ID_START + 0x02);
|
||||
int var_call_return_1 = (VAR_ID_START + 0x03);
|
||||
|
||||
mystery_gift_script::mystery_gift_script()
|
||||
mystery_gift_script::mystery_gift_script(PokemonTables &data_tables)
|
||||
: data_tables(data_tables)
|
||||
{
|
||||
curr_mg_index = NPC_LOCATION_OFFSET;
|
||||
curr_section30_index = 0;
|
||||
|
|
@ -331,7 +332,7 @@ void mystery_gift_script::build_script(Pokemon_Party &incoming_box_data)
|
|||
|
||||
for (int i = 0; i < MAX_PKMN_IN_BOX; i++) // Add in the Pokemon data
|
||||
{
|
||||
Pokemon curr_pkmn = incoming_box_data.get_converted_pkmn(i);
|
||||
Pokemon curr_pkmn = incoming_box_data.get_converted_pkmn(data_tables, i);
|
||||
if (curr_pkmn.get_validity())
|
||||
{
|
||||
for (int curr_byte = 0; curr_byte < POKEMON_SIZE; curr_byte++)
|
||||
|
|
@ -354,14 +355,16 @@ void mystery_gift_script::build_script(Pokemon_Party &incoming_box_data)
|
|||
}
|
||||
|
||||
// insert text
|
||||
textThank.insert_text(save_section_30);
|
||||
textPCFull.insert_text(save_section_30);
|
||||
textWeHere.insert_text(save_section_30);
|
||||
textPCConvo.insert_text(save_section_30);
|
||||
textPCThanks.insert_text(save_section_30);
|
||||
textLookerFull.insert_text(save_section_30);
|
||||
textMoveBox.insert_text(save_section_30);
|
||||
textReceived.insert_text(save_section_30);
|
||||
data_tables.load_gen3_charset(ENG_ID);
|
||||
|
||||
textThank.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textPCFull.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textWeHere.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textPCConvo.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textPCThanks.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textLookerFull.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textMoveBox.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
textReceived.insert_text(data_tables.gen3_charset, save_section_30);
|
||||
|
||||
movementSlowSpin.insert_movement(save_section_30);
|
||||
movementFastSpin.insert_movement(save_section_30);
|
||||
|
|
@ -783,9 +786,9 @@ void mystery_gift_script::build_script(Pokemon_Party &incoming_box_data)
|
|||
add_word(flashBuffer_ptr.place_word());
|
||||
add_word(readFlashSector_ptr.place_word());
|
||||
|
||||
textGreet.insert_virtual_text(mg_script);
|
||||
textYouMustBe.insert_virtual_text(mg_script);
|
||||
textIAm.insert_virtual_text(mg_script);
|
||||
textGreet.insert_virtual_text(data_tables.gen3_charset, mg_script);
|
||||
textYouMustBe.insert_virtual_text(data_tables.gen3_charset, mg_script);
|
||||
textIAm.insert_virtual_text(data_tables.gen3_charset, mg_script);
|
||||
|
||||
for (unsigned int i = 0; i < mg_variable_list.size(); i++) // Fill all the refrences for script variables in the mg
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ static u8 frlg_wonder_card[0x14E] = {
|
|||
|
||||
bool inject_mystery(Pokemon_Party &incoming_box_data)
|
||||
{
|
||||
mystery_gift_script script;
|
||||
PokemonTables data_tables;
|
||||
mystery_gift_script script(data_tables);
|
||||
if (ENABLE_OLD_EVENT)
|
||||
{
|
||||
// script.build_script_old(incoming_box_data);
|
||||
|
|
@ -83,7 +84,7 @@ bool inject_mystery(Pokemon_Party &incoming_box_data)
|
|||
{
|
||||
for (int i = 0; i < MAX_PKMN_IN_BOX; i++) // Add in the Pokemon data
|
||||
{
|
||||
Pokemon curr_pkmn = incoming_box_data.get_converted_pkmn(i);
|
||||
Pokemon curr_pkmn = incoming_box_data.get_converted_pkmn(data_tables, i);
|
||||
if (curr_pkmn.get_validity())
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#include "button_handler.h"
|
||||
#include "translated_text.h"
|
||||
#include "text_engine.h"
|
||||
#include "zx0_decompressor.h"
|
||||
#include "TYPES_zx0_bin.h"
|
||||
|
||||
Dex dex_array[DEX_MAX];
|
||||
int dex_shift = 0;
|
||||
|
|
@ -71,6 +73,11 @@ void pokedex_init()
|
|||
|
||||
int pokedex_loop()
|
||||
{
|
||||
u8 TYPES[POKEMON_ARRAY_SIZE * 2];
|
||||
|
||||
zx0_decompressor_set_input(TYPES_zx0_bin);
|
||||
zx0_decompressor_read((uint8_t*)TYPES, zx0_decompressor_get_decompressed_size());
|
||||
|
||||
pokedex_init();
|
||||
pokedex_show();
|
||||
bool update = true;
|
||||
|
|
@ -198,7 +205,7 @@ int pokedex_loop()
|
|||
// Eventually it could be optimized to move the labels around, but this honestly makes the most sense. Less code but one frame different
|
||||
for (int i = 0; i < DEX_MAX; i++)
|
||||
{
|
||||
load_type_sprites(dex_shift + i + 1 + mythic_skip, i, is_caught(dex_shift + i + 1 + mythic_skip));
|
||||
load_type_sprites(TYPES, dex_shift + i + 1 + mythic_skip, i, is_caught(dex_shift + i + 1 + mythic_skip));
|
||||
}
|
||||
update = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include "save_data_manager.h"
|
||||
#include "debug_mode.h"
|
||||
#include "text_engine.h"
|
||||
#include "zx0_decompressor.h"
|
||||
#include "JPN_NAMES_zx0_bin.h"
|
||||
|
||||
Pokemon::Pokemon() {};
|
||||
|
||||
|
|
@ -159,7 +161,7 @@ void Pokemon::load_data(int index, const byte *party_data, int game, int lang)
|
|||
global_next_frame();
|
||||
}
|
||||
}
|
||||
void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
||||
void Pokemon::convert_to_gen_three(PokemonTables& data_tables, bool simplified, bool stabilize_mythical)
|
||||
{
|
||||
// Convert the species indexes
|
||||
if (gen == 1)
|
||||
|
|
@ -170,7 +172,8 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
}
|
||||
else
|
||||
{
|
||||
species_index_struct = gen_1_index_array[species_index_struct];
|
||||
data_tables.load_gen1_index_array();
|
||||
species_index_struct = data_tables.gen_1_index_array[species_index_struct];
|
||||
if (species_index_struct == 0xFF)
|
||||
{
|
||||
is_missingno = true;
|
||||
|
|
@ -206,13 +209,21 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
// Set nickname
|
||||
if (language == KOR_ID)
|
||||
{
|
||||
|
||||
gen_3_pkmn[18] = JPN_ID; // Set to JPN
|
||||
byte new_nickname[10];
|
||||
byte new_ot[7];
|
||||
u16 cur_char;
|
||||
|
||||
data_tables.load_gen3_charset(language);
|
||||
// setup the zx0 decompressor to decompress the JPN_NAMES table
|
||||
zx0_decompressor_set_input(JPN_NAMES_zx0_bin);
|
||||
// seek to the right pokemon
|
||||
zx0_decompressor_seek(sizeof(u16) * 6 * species_index_struct);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{ // Read the JPN name and convert it
|
||||
new_nickname[i] = get_gen_3_char(JPN_NAMES[species_index_struct][i], true);
|
||||
zx0_decompressor_read((u8*)&cur_char, 2);
|
||||
new_nickname[i] = data_tables.get_gen_3_char(cur_char);
|
||||
}
|
||||
|
||||
if (gen == 1)
|
||||
|
|
@ -247,13 +258,16 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
}
|
||||
else
|
||||
{
|
||||
gen_3_pkmn[18] = language; // Language
|
||||
copy_from_to(convert_text(&nickname[0], 10, gen, language), &gen_3_pkmn[8], 10, false); // Nickname
|
||||
copy_from_to(convert_text(&trainer_name[0], 7, gen, language), &gen_3_pkmn[20], 7, false); // OT Name
|
||||
gen_3_pkmn[18] = language; // Language
|
||||
|
||||
data_tables.load_input_charset(gen, language);
|
||||
data_tables.load_gen3_charset(language);
|
||||
copy_from_to(convert_text(data_tables, &nickname[0], 10), &gen_3_pkmn[8], 10, false); // Nickname
|
||||
copy_from_to(convert_text(data_tables, &trainer_name[0], 7), &gen_3_pkmn[20], 7, false); // OT Name
|
||||
}
|
||||
|
||||
// Make sure Level is not over 100 based on EXP
|
||||
u32 max_exp = get_max_exp(species_index_struct);
|
||||
u32 max_exp = data_tables.get_max_exp(species_index_struct);
|
||||
if (exp > max_exp)
|
||||
{
|
||||
exp = max_exp;
|
||||
|
|
@ -320,7 +334,7 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((!can_learn_move(species_index_struct, moves[i])) && (moves[i] != 0))
|
||||
if ((!data_tables.can_learn_move(species_index_struct, moves[i])) && (moves[i] != 0))
|
||||
{
|
||||
moves[i] = 0; // Remove the move
|
||||
pp_bonus[i] = 0; // Remove the PP bonus
|
||||
|
|
@ -331,7 +345,7 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
// Make sure it has at least one move
|
||||
if (moves[0] + moves[1] + moves[2] + moves[3] == 0)
|
||||
{
|
||||
moves[0] = get_earliest_move(species_index_struct);
|
||||
moves[0] = data_tables.get_earliest_move(species_index_struct);
|
||||
}
|
||||
|
||||
// Bubble valid moves to the top
|
||||
|
|
@ -360,15 +374,16 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
}
|
||||
|
||||
// Restore the PP values
|
||||
data_tables.load_power_points();
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
pure_pp_values[i] = POWER_POINTS[moves[i]] + ((POWER_POINTS[moves[i]] / 5) * pp_bonus[i]);
|
||||
pure_pp_values[i] = data_tables.POWER_POINTS[moves[i]] + ((data_tables.POWER_POINTS[moves[i]] / 5) * pp_bonus[i]);
|
||||
}
|
||||
|
||||
// This is everything the mythical needs, don't change anything else
|
||||
if (stabilize_mythical && (species_index_struct == 151 || species_index_struct == 251))
|
||||
{
|
||||
set_to_event(nature_mod);
|
||||
set_to_event(data_tables, nature_mod);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +392,7 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
u32 n_pid;
|
||||
if (ENABLE_MATCH_PID)
|
||||
{
|
||||
n_pid = generate_pid_iv_match(species_index_struct, nature_mod, &dvs[0]);
|
||||
n_pid = generate_pid_iv_match(data_tables, species_index_struct, nature_mod, &dvs[0]);
|
||||
|
||||
u16 curr_rand = get_rand_u16();
|
||||
ivs[0] = (curr_rand >> 0) & 0b11111;
|
||||
|
|
@ -395,7 +410,7 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
}
|
||||
else
|
||||
{
|
||||
n_pid = generate_pid_save_iv(species_index_struct, nature_mod, &dvs[0]);
|
||||
n_pid = generate_pid_save_iv(data_tables, species_index_struct, nature_mod, &dvs[0]);
|
||||
|
||||
// Convert and set IVs
|
||||
int hp_iv = 0;
|
||||
|
|
@ -420,7 +435,7 @@ void Pokemon::convert_to_gen_three(bool simplified, bool stabilize_mythical)
|
|||
enable_auto_random();
|
||||
|
||||
// Determine and set Ability
|
||||
iv_egg_ability |= ((pid[0] & 0x1) ? get_num_abilities(species_index_struct) : 0) << 31;
|
||||
iv_egg_ability |= ((pid[0] & 0x1) ? data_tables.get_num_abilities(species_index_struct) : 0) << 31;
|
||||
|
||||
// Origin info
|
||||
origin_info |= ((caught_data[1] & 0b10000000) << 8); // OT gender - We would shift left 15 bits, but the bit is already shifted over 7
|
||||
|
|
@ -672,66 +687,23 @@ byte Pokemon::get_unencrypted_data(int index)
|
|||
return unencrypted_data[index];
|
||||
}
|
||||
|
||||
byte *Pokemon::convert_text(byte *text_array, int size, int gen, int lang)
|
||||
byte *Pokemon::convert_text(PokemonTables& data_tables, byte *text_array, int size)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
switch (gen)
|
||||
{
|
||||
case 1:
|
||||
switch (lang)
|
||||
{
|
||||
case JPN_ID:
|
||||
text_array[i] = get_gen_3_char(gen_1_Jpn_char_array[text_array[i]], true);
|
||||
break;
|
||||
case ENG_ID:
|
||||
default:
|
||||
text_array[i] = get_gen_3_char(gen_1_Eng_char_array[text_array[i]], false);
|
||||
break;
|
||||
case FRE_ID:
|
||||
case GER_ID:
|
||||
text_array[i] = get_gen_3_char(gen_1_FreGer_char_array[text_array[i]], false);
|
||||
break;
|
||||
case SPA_ID:
|
||||
case ITA_ID:
|
||||
text_array[i] = get_gen_3_char(gen_1_ItaSpa_char_array[text_array[i]], false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
switch (lang)
|
||||
{
|
||||
case JPN_ID:
|
||||
text_array[i] = get_gen_3_char(gen_2_Jpn_char_array[text_array[i]], true);
|
||||
break;
|
||||
case ENG_ID:
|
||||
default:
|
||||
text_array[i] = get_gen_3_char(gen_2_Eng_char_array[text_array[i]], false);
|
||||
break;
|
||||
case FRE_ID:
|
||||
case GER_ID:
|
||||
text_array[i] = get_gen_3_char(gen_2_FreGer_char_array[text_array[i]], false);
|
||||
break;
|
||||
case SPA_ID:
|
||||
case ITA_ID:
|
||||
text_array[i] = get_gen_3_char(gen_2_ItaSpa_char_array[text_array[i]], false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
text_array[i] = data_tables.get_gen_3_char(data_tables.input_charset[text_array[i]]);
|
||||
}
|
||||
return text_array;
|
||||
}
|
||||
|
||||
u32 Pokemon::generate_pid_iv_match(byte pid_species_index, byte nature, byte *pid_dvs)
|
||||
u32 Pokemon::generate_pid_iv_match(PokemonTables& data_tables, byte pid_species_index, byte nature, byte *pid_dvs)
|
||||
{
|
||||
u32 new_pid = 0;
|
||||
byte new_nature = 0;
|
||||
byte new_gender = 0;
|
||||
byte new_letter = 0;
|
||||
int gen2_gender_threshold = get_gender_threshold(pid_species_index, false);
|
||||
int gen3_gender_threshold = get_gender_threshold(pid_species_index, true);
|
||||
int gen2_gender_threshold = data_tables.get_gender_threshold(pid_species_index, false);
|
||||
int gen3_gender_threshold = data_tables.get_gender_threshold(pid_species_index, true);
|
||||
bool gender = (((pid_dvs[0] >> 4) & 0b1111) < gen2_gender_threshold);
|
||||
|
||||
do
|
||||
|
|
@ -768,7 +740,7 @@ u8 Pokemon::get_gender_from_pid(u32 pid)
|
|||
return (pid & 0xFF);
|
||||
};
|
||||
|
||||
u32 Pokemon::generate_pid_save_iv(byte pid_species_index, byte nature, byte *pid_dvs)
|
||||
u32 Pokemon::generate_pid_save_iv(PokemonTables &data_tables, byte pid_species_index, byte nature, byte *pid_dvs)
|
||||
{
|
||||
// Set Unown Letter
|
||||
u32 new_pid = 0;
|
||||
|
|
@ -795,7 +767,7 @@ u32 Pokemon::generate_pid_save_iv(byte pid_species_index, byte nature, byte *pid
|
|||
else
|
||||
{
|
||||
// Set the correct gender for the Pokemon
|
||||
new_pid |= get_rand_gender_byte(pid_species_index, ((pid_dvs[0] >> 4) & 0b1111));
|
||||
new_pid |= get_rand_gender_byte(data_tables, pid_species_index, ((pid_dvs[0] >> 4) & 0b1111));
|
||||
|
||||
// Randomize rest of PID
|
||||
new_pid |= get_rand_u32() & 0xFFFFFF00;
|
||||
|
|
@ -814,10 +786,10 @@ byte Pokemon::rand_reverse_mod(byte modulo_divisor, byte target_mod)
|
|||
return (modulo_divisor * get_rand_range(0, (255 - target_mod) / modulo_divisor)) + target_mod;
|
||||
}
|
||||
|
||||
byte Pokemon::get_rand_gender_byte(byte index_num, byte attack_DVs)
|
||||
byte Pokemon::get_rand_gender_byte(PokemonTables &data_tables, byte index_num, byte attack_DVs)
|
||||
{
|
||||
byte gen2_threshold = get_gender_threshold(index_num, false);
|
||||
byte gen3_threshold = get_gender_threshold(index_num, true);
|
||||
byte gen2_threshold = data_tables.get_gender_threshold(index_num, false);
|
||||
byte gen3_threshold = data_tables.get_gender_threshold(index_num, true);
|
||||
if (gen2_threshold == -1) // Is one gender or is genderless
|
||||
{
|
||||
return get_rand_range(0, 256);
|
||||
|
|
@ -869,7 +841,7 @@ Simplified_Pokemon Pokemon::get_simple_pkmn()
|
|||
return curr_pkmn;
|
||||
}
|
||||
|
||||
void Pokemon::set_to_event(byte nature)
|
||||
void Pokemon::set_to_event(PokemonTables &data_tables, byte nature)
|
||||
{
|
||||
int event_id = 0;
|
||||
if (species_index_struct == 151)
|
||||
|
|
@ -915,6 +887,7 @@ void Pokemon::set_to_event(byte nature)
|
|||
}
|
||||
}
|
||||
|
||||
data_tables.load_event_pkmn();
|
||||
// Load the event into the Pokemon array and unencrypted data array
|
||||
for (int i = 0; i < 0x20; i++)
|
||||
{
|
||||
|
|
@ -922,15 +895,15 @@ void Pokemon::set_to_event(byte nature)
|
|||
{
|
||||
i += 10; // Skip over the nickname
|
||||
}
|
||||
gen_3_pkmn[i] = EVENT_PKMN[event_id][i];
|
||||
gen_3_pkmn[i] = data_tables.EVENT_PKMN[event_id][i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
data_section_G[i] = EVENT_PKMN[event_id][i + 0x20 + 0];
|
||||
data_section_A[i] = EVENT_PKMN[event_id][i + 0x20 + 12];
|
||||
data_section_E[i] = EVENT_PKMN[event_id][i + 0x20 + 24];
|
||||
data_section_M[i] = EVENT_PKMN[event_id][i + 0x20 + 36];
|
||||
data_section_G[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 0];
|
||||
data_section_A[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 12];
|
||||
data_section_E[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 24];
|
||||
data_section_M[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 36];
|
||||
}
|
||||
|
||||
// insert moves and PP bonuses
|
||||
|
|
@ -975,7 +948,8 @@ void Pokemon::set_to_event(byte nature)
|
|||
enable_auto_random();
|
||||
|
||||
// Determine and set Ability
|
||||
iv_egg_ability |= ((pid[0] & 0x1) ? get_num_abilities(species_index_struct) : 0) << 31;
|
||||
data_tables.load_num_abilities();
|
||||
iv_egg_ability |= ((pid[0] & 0x1) ? data_tables.get_num_abilities(species_index_struct) : 0) << 31;
|
||||
|
||||
// Set IVs, Egg, and Ability
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -204,11 +204,11 @@ int Pokemon_Party::get_last_error()
|
|||
return last_error;
|
||||
}
|
||||
|
||||
Pokemon Pokemon_Party::get_converted_pkmn(int index)
|
||||
Pokemon Pokemon_Party::get_converted_pkmn(PokemonTables& data_tables, int index)
|
||||
{
|
||||
Pokemon converted_mon;
|
||||
converted_mon.load_data(index, box_data_array, game, lang);
|
||||
converted_mon.convert_to_gen_three(false, stabilize_mythic);
|
||||
converted_mon.convert_to_gen_three(data_tables, false, stabilize_mythic);
|
||||
has_new_pkmn = has_new_pkmn || converted_mon.get_is_new();
|
||||
simple_pkmn_array[index] = converted_mon.get_simple_pkmn();
|
||||
return converted_mon;
|
||||
|
|
@ -281,14 +281,14 @@ Simplified_Pokemon Pokemon_Party::get_simple_pkmn(int index)
|
|||
return simple_pkmn_array[index];
|
||||
}
|
||||
|
||||
bool Pokemon_Party::fill_simple_pkmn_array()
|
||||
bool Pokemon_Party::fill_simple_pkmn_array(PokemonTables &data_tables)
|
||||
{
|
||||
contains_mythical = false;
|
||||
for (int index = 0; index < get_num_pkmn(); index++)
|
||||
{
|
||||
Pokemon converted_mon;
|
||||
converted_mon.load_data(index, box_data_array, game, lang);
|
||||
converted_mon.convert_to_gen_three(true, stabilize_mythic);
|
||||
converted_mon.convert_to_gen_three(data_tables, true, stabilize_mythic);
|
||||
has_new_pkmn = has_new_pkmn || converted_mon.get_is_new();
|
||||
contains_mythical = contains_mythical ||
|
||||
converted_mon.get_dex_number() == 151 || converted_mon.get_dex_number() == 251;
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ void populate_lang_menu()
|
|||
langs.add_option(option_german, GER_ID);
|
||||
langs.add_option(option_italian, ITA_ID);
|
||||
langs.add_option(option_korean, KOR_ID);
|
||||
langs.add_option(option_cancel, -1);
|
||||
langs.add_option(option_cancel, UINT8_MAX);
|
||||
}
|
||||
|
||||
void populate_game_menu(int lang)
|
||||
|
|
@ -154,13 +154,13 @@ void populate_game_menu(int lang)
|
|||
games.add_option(option_gold, GOLD_ID);
|
||||
games.add_option(option_silver, SILVER_ID);
|
||||
games.add_option(option_crystal, CRYSTAL_ID);
|
||||
games.add_option(option_cancel, -1);
|
||||
games.add_option(option_cancel, UINT8_MAX);
|
||||
break;
|
||||
|
||||
case (KOR_ID):
|
||||
games.add_option(option_gold, GOLD_ID);
|
||||
games.add_option(option_silver, SILVER_ID);
|
||||
games.add_option(option_cancel, -1);
|
||||
games.add_option(option_cancel, UINT8_MAX);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -170,7 +170,7 @@ void populate_game_menu(int lang)
|
|||
games.add_option(option_gold, GOLD_ID);
|
||||
games.add_option(option_silver, SILVER_ID);
|
||||
games.add_option(option_crystal, CRYSTAL_ID);
|
||||
games.add_option(option_cancel, -1);
|
||||
games.add_option(option_cancel, UINT8_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -300,8 +300,8 @@ bool run_conditional(int index)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
games.set_lang(lang);
|
||||
party_data.set_lang(lang);
|
||||
games.set_lang(static_cast<u8>(lang));
|
||||
party_data.set_lang(static_cast<u8>(lang));
|
||||
return true;
|
||||
|
||||
case CMD_GAME_MENU:
|
||||
|
|
@ -348,8 +348,10 @@ bool run_conditional(int index)
|
|||
return true;
|
||||
|
||||
case CMD_LOAD_SIMP:
|
||||
return party_data.fill_simple_pkmn_array();
|
||||
|
||||
{
|
||||
PokemonTables data_tables;
|
||||
return party_data.fill_simple_pkmn_array(data_tables);
|
||||
}
|
||||
case CMD_CANCEL_LINK:
|
||||
party_data.continue_link(true);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -4,27 +4,29 @@
|
|||
#include "pokemon.h"
|
||||
#include "pokemon_party.h"
|
||||
#include "script_array.h"
|
||||
#include "translated_text.h"
|
||||
|
||||
script_obj::script_obj(){};
|
||||
|
||||
script_obj::script_obj(const byte* nText, int nNext)
|
||||
script_obj::script_obj(const byte* nText, uint16_t nNext)
|
||||
{
|
||||
text = nText;
|
||||
has_text = true;
|
||||
next_index = nNext;
|
||||
conditional_index = 0;
|
||||
next_false_index = 0;
|
||||
}
|
||||
|
||||
script_obj::script_obj(int nRun, int nNext)
|
||||
script_obj::script_obj(uint16_t nRun, uint16_t nNext)
|
||||
{
|
||||
text = nullptr;
|
||||
next_index = nNext;
|
||||
conditional_index = nRun;
|
||||
next_false_index = nNext;
|
||||
}
|
||||
|
||||
script_obj::script_obj(int nRun, int nNext_if_true, int nNext_if_false)
|
||||
script_obj::script_obj(uint16_t nRun, uint16_t nNext_if_true, uint16_t nNext_if_false)
|
||||
{
|
||||
text = nullptr;
|
||||
next_index = nNext_if_true;
|
||||
conditional_index = nRun;
|
||||
next_false_index = nNext_if_false;
|
||||
|
|
@ -32,27 +34,20 @@ script_obj::script_obj(int nRun, int nNext_if_true, int nNext_if_false)
|
|||
|
||||
const byte* script_obj::get_text()
|
||||
{
|
||||
if (has_text)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
int script_obj::get_true_index()
|
||||
uint16_t script_obj::get_true_index()
|
||||
{
|
||||
return next_index;
|
||||
}
|
||||
|
||||
int script_obj::get_false_index()
|
||||
uint16_t script_obj::get_false_index()
|
||||
{
|
||||
return next_false_index;
|
||||
}
|
||||
|
||||
int script_obj::get_cond_id()
|
||||
uint16_t script_obj::get_cond_id()
|
||||
{
|
||||
return conditional_index;
|
||||
}
|
||||
|
|
@ -153,12 +153,12 @@ void textbox_var::set_virtual_start()
|
|||
start_location_in_script = *curr_loc_ptr - 4;
|
||||
}
|
||||
|
||||
void textbox_var::insert_text(u8 mg_array[])
|
||||
void textbox_var::insert_text(const u16 *charset, u8 mg_array[])
|
||||
{
|
||||
set_start();
|
||||
for (int parser = 0; parser < text_length; parser++)
|
||||
{
|
||||
if (curr_rom.is_hoenn() && (text[parser] == 0xFC) && (get_gen_3_char((char16_t)(text[parser + 1]), false) == 0x01)) // Removes colored text
|
||||
if (curr_rom.is_hoenn() && (text[parser] == 0xFC) && (get_char_from_charset(charset, (char16_t)(text[parser + 1])) == 0x01)) // Removes colored text
|
||||
{
|
||||
parser += 2;
|
||||
}
|
||||
|
|
@ -172,12 +172,12 @@ void textbox_var::insert_text(u8 mg_array[])
|
|||
(*curr_loc_ptr)++;
|
||||
}
|
||||
|
||||
void textbox_var::insert_virtual_text(u8 mg_array[])
|
||||
void textbox_var::insert_virtual_text(const u16 *charset, u8 mg_array[])
|
||||
{
|
||||
set_virtual_start();
|
||||
for (int parser = 0; parser < text_length; parser++)
|
||||
{
|
||||
if (curr_rom.is_hoenn() && (text[parser] == 0xFC) && (get_gen_3_char((char16_t)(text[parser + 1]), false) == 0x01)) // Removes colored text
|
||||
if (curr_rom.is_hoenn() && (text[parser] == 0xFC) && (get_char_from_charset(charset, (char16_t)(text[parser + 1])) == 0x01)) // Removes colored text
|
||||
{
|
||||
parser += 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#define TILE_HEIGHT 8
|
||||
#define TILE_WIDTH 8
|
||||
|
||||
Select_Menu::Select_Menu(bool enable_cancel, int nMenu_type, int nStartTileX, int nStartTileY)
|
||||
Select_Menu::Select_Menu(bool enable_cancel, u8 nMenu_type, int nStartTileX, int nStartTileY)
|
||||
{
|
||||
cancel_enabled = enable_cancel;
|
||||
menu_type = nMenu_type;
|
||||
|
|
@ -14,7 +14,7 @@ Select_Menu::Select_Menu(bool enable_cancel, int nMenu_type, int nStartTileX, in
|
|||
startTileY = nStartTileY;
|
||||
}
|
||||
|
||||
void Select_Menu::add_option(const byte *option, int return_value)
|
||||
void Select_Menu::add_option(const byte *option, u8 return_value)
|
||||
{
|
||||
menu_options.push_back(option);
|
||||
return_values.push_back(return_value);
|
||||
|
|
@ -66,7 +66,7 @@ int Select_Menu::select_menu_main()
|
|||
|
||||
if (update)
|
||||
{
|
||||
if (return_values[curr_selection] == -1)
|
||||
if (return_values[curr_selection] == UINT8_MAX)
|
||||
{
|
||||
switch (menu_type)
|
||||
{
|
||||
|
|
@ -140,7 +140,7 @@ void Select_Menu::clear_options()
|
|||
return_values.clear();
|
||||
}
|
||||
|
||||
void Select_Menu::set_lang(int nLang)
|
||||
void Select_Menu::set_lang(u8 nLang)
|
||||
{
|
||||
lang = nLang;
|
||||
}
|
||||
|
|
@ -514,13 +514,13 @@ void load_temp_box_sprites(Pokemon_Party *party_data)
|
|||
load_sprite_compressed(button_confirm_right, button_edgeTiles, curr_tile_id, BTN_PAL, ATTR0_TALL, ATTR1_SIZE_8x32, 1);
|
||||
}
|
||||
|
||||
void load_type_sprites(int pkmn_index, int dex_offset, bool is_caught)
|
||||
void load_type_sprites(const u8* pkmn_type_table, int pkmn_index, int dex_offset, bool is_caught)
|
||||
{
|
||||
if (is_caught)
|
||||
{
|
||||
u32 curr_tile_id = global_tile_id_end + (dex_offset * 2 * 4);
|
||||
int type1 = TYPES[pkmn_index][0];
|
||||
int type2 = TYPES[pkmn_index][1];
|
||||
u16 type1 = pkmn_type_table[pkmn_index * 2];
|
||||
u16 type2 = pkmn_type_table[pkmn_index * 2 + 1];
|
||||
|
||||
load_sprite(type_sprites[(dex_offset * 2) + 0], &typesTiles[(type1 * 32)], 128, curr_tile_id, (type1 < 13 ? TYPES_PAL1 : TYPES_PAL2), ATTR0_WIDE, ATTR1_SIZE_32x8, 1);
|
||||
load_sprite(type_sprites[(dex_offset * 2) + 1], &typesTiles[(type2 * 32)], 128, curr_tile_id, (type2 < 13 ? TYPES_PAL1 : TYPES_PAL2), ATTR0_WIDE, ATTR1_SIZE_32x8, 1);
|
||||
|
|
@ -561,7 +561,7 @@ void load_sprite_compressed(OBJ_ATTR *sprite, const unsigned int objTiles[],
|
|||
obj_hide(sprite);
|
||||
};
|
||||
|
||||
void load_select_sprites(int game_id, int lang)
|
||||
void load_select_sprites(u8 game_id, u8 lang)
|
||||
{
|
||||
u32 curr_tile_id = global_tile_id_end;
|
||||
// Alpha Shadow Main Color Grey Black Mid
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ int ptgb_write(const byte *text, bool instant, int length)
|
|||
return 0; // str - text;
|
||||
}
|
||||
// This is mostly used for debug stuff, I shouldn't rely it on it much.
|
||||
int ptgb_write_debug(const char *text, bool instant)
|
||||
int ptgb_write_debug(const u16* charset, const char *text, bool instant)
|
||||
{
|
||||
byte temp_holding[256];
|
||||
int i;
|
||||
|
|
@ -240,7 +240,7 @@ int ptgb_write_debug(const char *text, bool instant)
|
|||
}
|
||||
else
|
||||
{
|
||||
temp_holding[i] = get_gen_3_char(text[i], false);
|
||||
temp_holding[i] = get_char_from_charset(charset, text[i]);
|
||||
}
|
||||
}
|
||||
return ptgb_write(temp_holding, instant);
|
||||
|
|
|
|||
475
source/zx0_decompressor.cpp
Normal file
475
source/zx0_decompressor.cpp
Normal file
|
|
@ -0,0 +1,475 @@
|
|||
#include "zx0_decompressor.h"
|
||||
#include <cstring>
|
||||
|
||||
// The following code is a custom implementation of the ZX0 decompression algorithm invented by Einar Saukas
|
||||
// Original implementation can be found here: https://github.com/einar-saukas/ZX0
|
||||
// It uses classes, but keeps them completely hidden in the .cpp file with an anonymous namespace for internal linkage.
|
||||
// The header provides a C facade to access the relevant methods, but the rest of Poke Transporter GB
|
||||
// doesn't need to be aware of all the datatypes/classes defined here.
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* This class implements a ringbuffer. This is useful for ZX0 decompression
|
||||
* because it allows you to look back easily.
|
||||
*
|
||||
* WARNING: the specified bufferSize MUST be a power of 2!
|
||||
* This is needed because I optimized a modulo division (for wraparound)
|
||||
* with a bitmask AND. (and it simply won't work correctly if the bufferSize is not a power of 2!)
|
||||
*/
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer(uint8_t *buffer, const uint16_t bufferSize);
|
||||
|
||||
/**
|
||||
* Returns the buffer size.
|
||||
*/
|
||||
uint16_t getBufferSize() const;
|
||||
|
||||
uint16_t read(uint8_t *outputBuffer, uint16_t bytesToRead);
|
||||
uint8_t readByte();
|
||||
void writeByte(uint8_t value);
|
||||
|
||||
/**
|
||||
* This function seeks backwards from the current end of the buffer
|
||||
*/
|
||||
void seekBackwardsFromBufferEnd(uint16_t offset);
|
||||
|
||||
void reset();
|
||||
protected:
|
||||
private:
|
||||
uint8_t *buffer_;
|
||||
uint16_t ringStartPos_;
|
||||
uint16_t ringReadPos_;
|
||||
uint16_t ringEndPos_;
|
||||
uint16_t bufferSize_;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class makes reading on a per-bit basis much easier.
|
||||
*/
|
||||
class BitReader
|
||||
{
|
||||
public:
|
||||
BitReader(const uint8_t* buffer);
|
||||
|
||||
uint8_t readBit();
|
||||
uint8_t read(uint8_t numBits);
|
||||
uint8_t readByte();
|
||||
protected:
|
||||
private:
|
||||
const uint8_t* buffer_;
|
||||
const uint8_t* curBuffer_;
|
||||
uint8_t currentByte_;
|
||||
uint8_t bitsLeft_;
|
||||
};
|
||||
|
||||
enum class ZX0OperationType
|
||||
{
|
||||
NONE,
|
||||
LITERAL_BLOCK,
|
||||
COPY_LAST_OFFSET,
|
||||
COPY_NEW_OFFSET
|
||||
};
|
||||
|
||||
typedef struct ZX0Command
|
||||
{
|
||||
ZX0OperationType cmdType;
|
||||
uint16_t length;
|
||||
uint16_t offset;
|
||||
uint16_t bytePos;
|
||||
} ZX0Command;
|
||||
|
||||
/**
|
||||
* @brief This class implements the actual ZX0 decompression.
|
||||
*/
|
||||
class ZX0Decompressor
|
||||
{
|
||||
public:
|
||||
ZX0Decompressor(uint8_t* decompressionBuffer, uint16_t decompressionBufferSize);
|
||||
|
||||
/**
|
||||
* @brief This function prepares the ZX0Decompressor instance
|
||||
* for decompressing the specified inputData
|
||||
* @param compressedData
|
||||
*/
|
||||
void setInput(const uint8_t *inputData);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the size of the data when it is fully decompressed
|
||||
* This is read from the first 2 bytes of the inputData
|
||||
*/
|
||||
uint16_t getDecompressedSize() const;
|
||||
|
||||
/**
|
||||
* @brief This function reads <numBytes> of data into <outputBuffer>
|
||||
*/
|
||||
uint16_t read(uint8_t *outputBuffer, uint16_t numBytes);
|
||||
|
||||
/**
|
||||
* @brief This function uncompresses from the current point until the
|
||||
* specified outputBytePos lies inside the decompressionBuffer
|
||||
*/
|
||||
void seek(uint16_t outputBytePos);
|
||||
protected:
|
||||
private:
|
||||
void readNextCommand();
|
||||
uint16_t copy_block(uint8_t *outputBuffer, uint16_t numBytes);
|
||||
|
||||
RingBuffer buffer_;
|
||||
BitReader reader_;
|
||||
ZX0Command cur_command_;
|
||||
const uint8_t *inputData_;
|
||||
uint16_t bytesDecompressed_;
|
||||
uint16_t lastOffset_;
|
||||
};
|
||||
|
||||
RingBuffer::RingBuffer(uint8_t *buffer, const uint16_t bufferSize)
|
||||
: buffer_(buffer)
|
||||
, ringStartPos_(0)
|
||||
, ringReadPos_(0)
|
||||
, ringEndPos_(0)
|
||||
, bufferSize_(bufferSize)
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
uint16_t RingBuffer::getBufferSize() const
|
||||
{
|
||||
return bufferSize_;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
uint16_t RingBuffer::read(uint8_t *outputBuffer, uint16_t bytesToRead)
|
||||
{
|
||||
if(ringReadPos_ == ringEndPos_) return 0; // Early exit if empty
|
||||
|
||||
const uint16_t wrapMask = bufferSize_ - 1;
|
||||
uint16_t available;
|
||||
uint16_t bytesRead;
|
||||
uint16_t chunkSize;
|
||||
|
||||
available = (ringReadPos_ < ringEndPos_) ? (ringEndPos_ - ringReadPos_) : (bufferSize_ - ringReadPos_);
|
||||
|
||||
chunkSize = (bytesToRead < available) ? bytesToRead : available;
|
||||
memcpy(outputBuffer, buffer_ + ringReadPos_, chunkSize);
|
||||
bytesRead = chunkSize;
|
||||
ringReadPos_ = (ringReadPos_ + chunkSize) & wrapMask; // wraparound done by bitmask
|
||||
bytesToRead -= chunkSize;
|
||||
|
||||
if(bytesToRead > 0 && ringReadPos_ != ringEndPos_)
|
||||
{
|
||||
// we need more bytes from the start of the buffer
|
||||
available = ringEndPos_;
|
||||
chunkSize = (bytesToRead <= ringEndPos_) ? bytesToRead : available;
|
||||
memcpy(outputBuffer + bytesRead, buffer_, chunkSize);
|
||||
bytesRead += chunkSize;
|
||||
ringReadPos_ = chunkSize & wrapMask; // Since we wrapped around to start
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
uint8_t RingBuffer::readByte()
|
||||
{
|
||||
uint8_t value;
|
||||
if(ringReadPos_ == ringEndPos_) return 0; // Early exit if empty
|
||||
|
||||
value = buffer_[ringReadPos_];
|
||||
ringReadPos_ = (ringReadPos_ + 1) & (bufferSize_ - 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
void RingBuffer::writeByte(uint8_t value)
|
||||
{
|
||||
buffer_[ringEndPos_] = value;
|
||||
ringEndPos_ = (ringEndPos_ + 1) & (bufferSize_ - 1); // wraparound done by bitmask
|
||||
|
||||
if(ringEndPos_ == ringStartPos_)
|
||||
{
|
||||
// buffer is full, overwrite oldest byte
|
||||
ringStartPos_ = (ringStartPos_ + 1) & (bufferSize_ - 1); // wraparound done by bitmask
|
||||
}
|
||||
}
|
||||
|
||||
void RingBuffer::seekBackwardsFromBufferEnd(uint16_t offset)
|
||||
{
|
||||
ringReadPos_ = (ringEndPos_ - offset) & (bufferSize_ - 1);
|
||||
}
|
||||
|
||||
void RingBuffer::reset()
|
||||
{
|
||||
ringStartPos_ = 0;
|
||||
ringReadPos_ = 0;
|
||||
ringEndPos_ = 0;
|
||||
}
|
||||
|
||||
static inline uint16_t read_elias_gamma(BitReader& reader)
|
||||
{
|
||||
uint16_t num_non_leading_bits = 0;
|
||||
uint16_t value;
|
||||
while (!reader.readBit())
|
||||
{
|
||||
++num_non_leading_bits; // Count leading zeros
|
||||
}
|
||||
// reconstruct the most significant bit of value
|
||||
value = 1 << num_non_leading_bits; // Start with MSB
|
||||
|
||||
// now apply the binary part (the actual value)
|
||||
while(num_non_leading_bits--)
|
||||
{
|
||||
value |= (reader.readBit() << num_non_leading_bits);
|
||||
}
|
||||
|
||||
// Adjust back to zero-based
|
||||
return value - 1;
|
||||
}
|
||||
|
||||
static inline void read_new_offset(BitReader& reader, uint16_t& offset)
|
||||
{
|
||||
const uint8_t has_msb = reader.readBit();
|
||||
|
||||
const uint16_t lsb = reader.read(7);
|
||||
const uint16_t msb = (has_msb) ? read_elias_gamma(reader) : 0;
|
||||
|
||||
offset = ((msb << 7) | lsb) + 1;
|
||||
}
|
||||
|
||||
BitReader::BitReader(const uint8_t* buffer)
|
||||
: buffer_(buffer)
|
||||
, curBuffer_(buffer)
|
||||
, currentByte_(0)
|
||||
, bitsLeft_(0)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t BitReader::readBit()
|
||||
{
|
||||
// Pre-decrement and check underflow
|
||||
if (--bitsLeft_ == 0xFF)
|
||||
{
|
||||
currentByte_ = (*curBuffer_);
|
||||
++curBuffer_;
|
||||
bitsLeft_ = 7;
|
||||
}
|
||||
return (currentByte_ >> bitsLeft_) & 1;
|
||||
}
|
||||
|
||||
uint8_t BitReader::read(uint8_t numBits)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
while (numBits--)
|
||||
{
|
||||
result = (result << 1) | readBit();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t BitReader::readByte()
|
||||
{
|
||||
if(bitsLeft_ == 0)
|
||||
{
|
||||
const uint8_t value = (*curBuffer_);
|
||||
++curBuffer_;
|
||||
return value;
|
||||
}
|
||||
// Handle byte reads across bit boundaries
|
||||
return read(8);
|
||||
}
|
||||
|
||||
ZX0Decompressor::ZX0Decompressor(uint8_t* decompressionBuffer, uint16_t decompressionBufferSize)
|
||||
: buffer_(decompressionBuffer, decompressionBufferSize)
|
||||
, reader_(nullptr)
|
||||
, cur_command_({ZX0OperationType::NONE, 0, 0, 0})
|
||||
, inputData_(nullptr)
|
||||
, bytesDecompressed_(0)
|
||||
, lastOffset_(UINT16_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
void ZX0Decompressor::setInput(const uint8_t *inputData)
|
||||
{
|
||||
buffer_.reset();
|
||||
reader_ = BitReader(inputData + 2);
|
||||
cur_command_ = {ZX0OperationType::NONE, 0, 0, 0};
|
||||
inputData_ = inputData;
|
||||
bytesDecompressed_ = 0;
|
||||
lastOffset_ = UINT16_MAX;
|
||||
}
|
||||
|
||||
uint16_t ZX0Decompressor::getDecompressedSize() const
|
||||
{
|
||||
if(!inputData_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return *((uint16_t*)inputData_);
|
||||
}
|
||||
|
||||
uint16_t ZX0Decompressor::read(uint8_t *outputBuffer, uint16_t numBytes)
|
||||
{
|
||||
const uint16_t decompressed_size = getDecompressedSize();
|
||||
const uint16_t bytesDecompressedBefore = bytesDecompressed_;
|
||||
uint16_t bytesRead;
|
||||
|
||||
while(numBytes)
|
||||
{
|
||||
if(bytesDecompressed_ == decompressed_size)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we have finished processing the previous pending command
|
||||
// if we have, we need to read a new operation
|
||||
if(cur_command_.cmdType == ZX0OperationType::NONE || cur_command_.bytePos >= cur_command_.length)
|
||||
{
|
||||
readNextCommand();
|
||||
}
|
||||
|
||||
bytesRead = copy_block(outputBuffer + bytesDecompressed_, numBytes);
|
||||
numBytes -= bytesRead;
|
||||
bytesDecompressed_ += bytesRead;
|
||||
}
|
||||
|
||||
return bytesDecompressed_ - bytesDecompressedBefore;
|
||||
}
|
||||
|
||||
void ZX0Decompressor::seek(uint16_t outputBytePos)
|
||||
{
|
||||
uint8_t read_buffer[32];
|
||||
uint16_t bytesToRead;
|
||||
uint16_t chunkSize;
|
||||
|
||||
// NOTE: outputBytePos denotes the index of the byte in the output (decompressed data) buffer!!
|
||||
|
||||
// ZX0 doesn't actually have random access
|
||||
// so we'll have to simulate it.
|
||||
// first check if the specified position is in the backwards direction.
|
||||
if(outputBytePos < bytesDecompressed_)
|
||||
{
|
||||
// the specified position is in earlier decoded data.
|
||||
// this will be expensive, because we will have to start decompression from the beginning
|
||||
// until the specified position.
|
||||
// reset the decoder
|
||||
setInput(inputData_);
|
||||
bytesToRead = outputBytePos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The specified position is in future -to-be-decoded- decompressed data.
|
||||
// so we just need to keep decoding until we're there.
|
||||
bytesToRead = outputBytePos - bytesDecompressed_;
|
||||
}
|
||||
|
||||
// Start decompressing until we're at the desired point
|
||||
do
|
||||
{
|
||||
chunkSize = (bytesToRead > sizeof(read_buffer)) ? sizeof(read_buffer) : bytesToRead;
|
||||
bytesToRead -= read(read_buffer, chunkSize);
|
||||
}
|
||||
while(bytesToRead);
|
||||
}
|
||||
|
||||
void ZX0Decompressor::readNextCommand()
|
||||
{
|
||||
const uint8_t cmdBit = reader_.readBit();
|
||||
|
||||
// the "COPY_NEW_OFFSET" command adds + 1 to the length, but the other commands don't.
|
||||
// given that read_elias_gamma() function is marked "inline", the way I set the length
|
||||
// is to avoid having multiple calls to it here. (for code size)
|
||||
if(cmdBit)
|
||||
{
|
||||
read_new_offset(reader_, lastOffset_);
|
||||
cur_command_.cmdType = ZX0OperationType::COPY_NEW_OFFSET;
|
||||
cur_command_.length = 1;
|
||||
cur_command_.offset = lastOffset_;
|
||||
cur_command_.bytePos = 0;
|
||||
}
|
||||
else if(cur_command_.cmdType == ZX0OperationType::LITERAL_BLOCK)
|
||||
{
|
||||
cur_command_.cmdType = ZX0OperationType::COPY_LAST_OFFSET;
|
||||
// copy from new offset and last offset differs in the sense that with the new offset the encoded length is reduced by one
|
||||
// and for last offset it isn't. This is likely because you still need to be able to insert a dummy "copy-from-last-offset" operation.
|
||||
cur_command_.length = 0;
|
||||
cur_command_.offset = lastOffset_;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_command_.cmdType = ZX0OperationType::LITERAL_BLOCK;
|
||||
cur_command_.length = 0;
|
||||
}
|
||||
cur_command_.length += read_elias_gamma(reader_);
|
||||
cur_command_.bytePos = 0;
|
||||
}
|
||||
|
||||
uint16_t ZX0Decompressor::copy_block(uint8_t *outputBuffer, uint16_t numBytes)
|
||||
{
|
||||
const uint16_t available = cur_command_.length - cur_command_.bytePos;
|
||||
const uint16_t bytesToRead = (numBytes > available) ? available : numBytes;
|
||||
uint16_t bytesRemaining = bytesToRead;
|
||||
|
||||
if(cur_command_.cmdType == ZX0OperationType::LITERAL_BLOCK)
|
||||
{
|
||||
// Literal copy
|
||||
do
|
||||
{
|
||||
(*outputBuffer) = reader_.readByte();
|
||||
buffer_.writeByte((*outputBuffer));
|
||||
++outputBuffer;
|
||||
}
|
||||
while(--bytesRemaining);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!cur_command_.bytePos)
|
||||
{
|
||||
buffer_.seekBackwardsFromBufferEnd(cur_command_.offset);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
*outputBuffer = buffer_.readByte();
|
||||
buffer_.writeByte(*outputBuffer);
|
||||
++outputBuffer;
|
||||
}
|
||||
while(--bytesRemaining);
|
||||
}
|
||||
|
||||
cur_command_.bytePos += bytesToRead;
|
||||
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// define a global 2 KB decompression buffer in IWRAM.
|
||||
// IWRAM is much faster than EXRAM, so it's ideally suited for decompression.
|
||||
// 2 KB is a modest/reasonable size to reserve.
|
||||
// But this also means we can only have one instance of ZX0Decompressor.
|
||||
// This is one of the reasons why it is implemented in the way that it is.
|
||||
__attribute__((section(".iwram")))
|
||||
static uint8_t decompression_buffer[2048];
|
||||
__attribute__((section(".iwram")))
|
||||
static ZX0Decompressor decompressor(decompression_buffer, sizeof(decompression_buffer));
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void zx0_decompressor_set_input(const uint8_t *input_data)
|
||||
{
|
||||
decompressor.setInput(input_data);
|
||||
}
|
||||
|
||||
uint16_t zx0_decompressor_get_decompressed_size()
|
||||
{
|
||||
return decompressor.getDecompressedSize();
|
||||
}
|
||||
|
||||
void zx0_decompressor_seek(uint16_t output_byte_pos)
|
||||
{
|
||||
decompressor.seek(output_byte_pos);
|
||||
}
|
||||
|
||||
uint16_t zx0_decompressor_read(uint8_t *output_buffer, uint16_t num_bytes)
|
||||
{
|
||||
return decompressor.read(output_buffer, num_bytes);
|
||||
}
|
||||
}
|
||||
43
tools/compressZX0/Makefile
Normal file
43
tools/compressZX0/Makefile
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# # Compiler flags
|
||||
CXXFLAGS := -std=c++11 -fno-rtti -fno-exceptions -fno-unwind-tables -Wall -Wextra -I $(CURDIR) -g
|
||||
|
||||
# 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))
|
||||
|
||||
# Ensure necessary directories exist
|
||||
# This function ensures the directory for the target exists
|
||||
define make_directory
|
||||
@mkdir -p $(dir $@)
|
||||
endef
|
||||
|
||||
# Target executable
|
||||
TARGET := compressZX0
|
||||
|
||||
# Phony targets
|
||||
.PHONY: all clean
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) $^ -o $@
|
||||
|
||||
# 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)
|
||||
433
tools/compressZX0/main.cpp
Normal file
433
tools/compressZX0/main.cpp
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
// @author risingPhil
|
||||
// This file implements the zx0 compression algorithm.
|
||||
// It serves as a test to see if this algorithm could suit us for the compression of several static arrays/buffers in Poke Transporter GB.
|
||||
|
||||
//#define LOG_OPERATIONS 1
|
||||
|
||||
#define MAX_OFFSET 2048 // Maximum backward offset (ZX0 limit), tuned for Poke Transporter GB to be able to use a 2 KB decompression buffer
|
||||
#define MAX_LEN 255 // Maximum match length (ZX0 limit)
|
||||
#define OUTPUT_BUFFER_SIZE 256 * 1024 // Maximum output buffer size (artificial limitation, I suppose)
|
||||
|
||||
#ifdef LOG_OPERATIONS
|
||||
#define LOG_OP(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_OP(...)
|
||||
#endif
|
||||
|
||||
// anonymous namespace for internal linkage
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* This class makes writing on a per-bit basis much easier
|
||||
*/
|
||||
class BitWriter
|
||||
{
|
||||
public:
|
||||
BitWriter(uint8_t* buffer);
|
||||
|
||||
void write(uint8_t value, uint8_t numBits);
|
||||
void write_bit(uint8_t value);
|
||||
void write_byte(uint8_t value);
|
||||
|
||||
size_t get_bits_written() const;
|
||||
protected:
|
||||
private:
|
||||
uint8_t* buffer_;
|
||||
uint8_t* cur_buffer_;
|
||||
uint8_t bit_index_;
|
||||
};
|
||||
|
||||
BitWriter::BitWriter(uint8_t* buffer)
|
||||
: buffer_(buffer)
|
||||
, cur_buffer_(buffer)
|
||||
, bit_index_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void BitWriter::write_bit(uint8_t value)
|
||||
{
|
||||
value &= 0x1;
|
||||
*cur_buffer_ |= (value << (7 - bit_index_));
|
||||
|
||||
if(bit_index_ == 7)
|
||||
{
|
||||
++cur_buffer_;
|
||||
bit_index_ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++bit_index_;
|
||||
}
|
||||
}
|
||||
|
||||
void BitWriter::write_byte(uint8_t value)
|
||||
{
|
||||
if(bit_index_)
|
||||
{
|
||||
write(value, 8);
|
||||
return;
|
||||
}
|
||||
*cur_buffer_ = value;
|
||||
++cur_buffer_;
|
||||
}
|
||||
|
||||
void BitWriter::write(uint8_t value, uint8_t numBits)
|
||||
{
|
||||
for(int i=numBits - 1; i >= 0; --i)
|
||||
{
|
||||
write_bit(value >> i);
|
||||
}
|
||||
}
|
||||
|
||||
size_t BitWriter::get_bits_written() const
|
||||
{
|
||||
return (cur_buffer_ - buffer_) * 8 + bit_index_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find the best match for the current position (LZ77-style)
|
||||
* We simply try to find the longest matching bytes backwards in the buffer.
|
||||
*/
|
||||
static void find_backwards_match(const unsigned char *buffer, size_t buffer_size, int pos, int *best_offset, int *best_len)
|
||||
{
|
||||
*best_offset = 0;
|
||||
*best_len = 0;
|
||||
const size_t max_offset = (pos > MAX_OFFSET) ? MAX_OFFSET : pos;
|
||||
const int max_len = (buffer_size - pos > MAX_LEN) ? MAX_LEN : buffer_size - pos;
|
||||
int len;
|
||||
|
||||
for (size_t offset = 1; offset <= max_offset; offset++)
|
||||
{
|
||||
len = 0;
|
||||
while (len < max_len && buffer[pos - offset + len] == buffer[pos + len])
|
||||
{
|
||||
++len;
|
||||
}
|
||||
if (len > *best_len)
|
||||
{
|
||||
*best_len = len;
|
||||
*best_offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function encodes the specified value with gamma encoding.
|
||||
*
|
||||
* The way it works is that we first determine of how many bits the value consists, except for the leading bits. (=num_non_leading_bits)
|
||||
* Then we write <num_non_leading_bits> zeros.
|
||||
* We also write the original value in <num_non_leading_bits + 1> bits
|
||||
*
|
||||
* For decoding, we can determine the number of zeros and that will indicate how many bits we need to read for the actual value.
|
||||
*
|
||||
*/
|
||||
static void write_elias_gamma(BitWriter& writer, int value)
|
||||
{
|
||||
value++; // Adjust because Gamma only encodes n ≥ 1
|
||||
int num_non_leading_bits = 0;
|
||||
int i;
|
||||
|
||||
// Calculate floor(log2(value))
|
||||
int tmp = value >> 1;
|
||||
while(tmp)
|
||||
{
|
||||
++num_non_leading_bits;
|
||||
tmp >>= 1;
|
||||
}
|
||||
|
||||
// Write unary part (k zeros)
|
||||
for (i = 0; i < num_non_leading_bits; i++)
|
||||
{
|
||||
writer.write_bit(0);
|
||||
}
|
||||
// Write binary part (num_non_leading_bits+1 bits of value)
|
||||
for (int i = num_non_leading_bits; i >= 0; i--)
|
||||
{
|
||||
writer.write_bit(value >> i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This struct represents a buffer to hold a number of pending "literal" bytes
|
||||
* before they actually get written to the output
|
||||
*/
|
||||
typedef struct LiteralBuffer
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
uint16_t size;
|
||||
} LiteralBuffer;
|
||||
|
||||
/**
|
||||
* @brief This function writes a command for the decompressor to start copying <length> bytes
|
||||
* from the last offset specified with the write_copy_from_new_offset_block() function
|
||||
*/
|
||||
static void write_copy_from_last_offset_block(BitWriter& writer, int length)
|
||||
{
|
||||
LOG_OP("copy_last: %d\n", length);
|
||||
writer.write_bit(0);
|
||||
write_elias_gamma(writer, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a command to copy the bytes in LiteralBuffer to the decompressed buffer.
|
||||
*/
|
||||
static void write_literal_block(BitWriter& writer, LiteralBuffer& literal_buffer)
|
||||
{
|
||||
uint16_t i = 0;
|
||||
|
||||
if(!literal_buffer.size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_OP("copy_literal: %hu\n", literal_buffer.size);
|
||||
|
||||
// flag that this is a literal block
|
||||
writer.write_bit(0);
|
||||
write_elias_gamma(writer, literal_buffer.size);
|
||||
|
||||
while(i < literal_buffer.size)
|
||||
{
|
||||
writer.write_byte(literal_buffer.buffer[i]);
|
||||
|
||||
++i;
|
||||
}
|
||||
literal_buffer.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a command to indicate that the decompressor must copy <length> bytes from the given backwards offset.
|
||||
*/
|
||||
static void write_copy_from_new_offset_block(BitWriter& writer, int offset, int length)
|
||||
{
|
||||
LOG_OP("copy_new: offset: %d, length: %d\n", offset, length);
|
||||
writer.write_bit(1); // Match flag
|
||||
|
||||
// Encode offset (Elias Gamma + 7-bit LSB)
|
||||
const int msb = ((offset - 1) >> 7) & 0xFF;
|
||||
const int lsb = (offset - 1) & 0x7F;
|
||||
|
||||
// first bit of LSB indicates whether the MSB follows.
|
||||
writer.write_bit((msb > 0));
|
||||
|
||||
// write 7 bit LSB raw bits
|
||||
writer.write(lsb, 7);
|
||||
|
||||
if (msb > 0)
|
||||
{
|
||||
write_elias_gamma(writer, msb);
|
||||
}
|
||||
|
||||
// Encode length (Elias Gamma)
|
||||
write_elias_gamma(writer, length - 1);
|
||||
}
|
||||
|
||||
static void literal_buffer_push(BitWriter& writer, LiteralBuffer& literal_buffer, uint8_t byte)
|
||||
{
|
||||
if(literal_buffer.size == 1024)
|
||||
{
|
||||
// EDGE case: buffer is full.
|
||||
// back-to-back literal blocks are forbidden,
|
||||
// so we must insert a dummy "use last offset" block
|
||||
write_literal_block(writer, literal_buffer);
|
||||
write_copy_from_last_offset_block(writer, 0);
|
||||
}
|
||||
|
||||
literal_buffer.buffer[literal_buffer.size] = byte;
|
||||
++literal_buffer.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function encodes the specified buffer with the ZX0 compression algorithm
|
||||
* and stores the result into output_buffer.
|
||||
*
|
||||
* Please make sure the output_buffer is sufficiently large enough before calling this function.
|
||||
*/
|
||||
static size_t encodeZX0(uint8_t* output_buffer, const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
BitWriter writer(output_buffer);
|
||||
LiteralBuffer literal_buffer = {
|
||||
.buffer = {0},
|
||||
.size = 0
|
||||
};
|
||||
|
||||
int pos = 0;
|
||||
int last_offset = 0x7FFFFFFF;
|
||||
int offset;
|
||||
int length;
|
||||
int numBytes = buffer_size;
|
||||
|
||||
// first write the size of the input in little endian format in the output buffer
|
||||
writer.write_byte(static_cast<uint8_t>(buffer_size));
|
||||
writer.write_byte(static_cast<uint8_t>(buffer_size >> 8));
|
||||
|
||||
while(pos < numBytes)
|
||||
{
|
||||
find_backwards_match(buffer, numBytes, pos, &offset, &length);
|
||||
|
||||
// important rules: You cannot have 2 consecutive literal blocks.
|
||||
// reusing the last offset can only happen after a literal block!
|
||||
|
||||
if(length < 2)
|
||||
{
|
||||
// we must buffer the literals because we can only start writing them when we know the "length"
|
||||
literal_buffer_push(writer, literal_buffer, buffer[pos]);
|
||||
++pos;
|
||||
}
|
||||
else if(offset == last_offset)
|
||||
{
|
||||
// write any pending literal bytes
|
||||
write_literal_block(writer, literal_buffer);
|
||||
write_copy_from_last_offset_block(writer, length);
|
||||
pos += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
// write any pending literal bytes
|
||||
write_literal_block(writer, literal_buffer);
|
||||
write_copy_from_new_offset_block(writer, offset, length);
|
||||
last_offset = offset;
|
||||
pos += length;
|
||||
}
|
||||
}
|
||||
|
||||
return writer.get_bits_written();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the given file completely into the specified buffer.
|
||||
* The buffer is allocated by this function, but should be delete[]'d by the caller.
|
||||
*/
|
||||
static bool read_file(const char* filename, uint8_t*& out_buffer, size_t& out_size)
|
||||
{
|
||||
FILE* file;
|
||||
long size;
|
||||
size_t read;
|
||||
uint8_t* buffer;
|
||||
|
||||
file = fopen(filename, "rb");
|
||||
if (!file) return false;
|
||||
|
||||
// Seek to end to determine size
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
{
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
size = ftell(file);
|
||||
if (size < 0)
|
||||
{
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
rewind(file);
|
||||
|
||||
buffer = new uint8_t[size];
|
||||
|
||||
read = fread(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
if (read != (size_t)size) {
|
||||
delete[] buffer;
|
||||
return false;
|
||||
}
|
||||
|
||||
out_buffer = buffer;
|
||||
out_size = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
const char* usageString = R"delim(
|
||||
Usage: compressZX0 <path/to/file> <output_path>
|
||||
|
||||
This program will compress the given file with the ZX0 compression algorithm and store the output in
|
||||
<output_path>/<filename_without_extension>_zx0.bin
|
||||
)delim";
|
||||
printf(usageString);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Reserve 256KB buffer, which is already much larger than the maximum file size we'd allow for PTGB.
|
||||
// (the reason why I'm using a buffer instead of writing directly to a file is simply because I'm lazy.
|
||||
// I wrote a test of the algorithm using buffers first. And I know that for Poke Transporter GB specifically
|
||||
// we'll never exceed the 256KB filesize. So I'm not going to rework this code, because there's currently no need)
|
||||
uint8_t output_buffer[OUTPUT_BUFFER_SIZE] = {0};
|
||||
uint8_t *input_buffer = nullptr;
|
||||
char *filename;
|
||||
char *extension_dot;
|
||||
size_t input_buffer_size;
|
||||
size_t bits_written;
|
||||
size_t num_bytes;
|
||||
double compress_ratio;
|
||||
char output_path[4096];
|
||||
FILE* f;
|
||||
|
||||
if(argc < 3)
|
||||
{
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!read_file(argv[1], input_buffer, input_buffer_size))
|
||||
{
|
||||
perror("Could not open file: ");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// make sure the input_buffer_size is not larger than our output_buffer we statically allocated
|
||||
// This is a bit of an artificial limitation though.
|
||||
if(input_buffer_size > sizeof(output_buffer))
|
||||
{
|
||||
fprintf(stderr, "ERROR: The input file should not be larger than %zu KB!\n", sizeof(output_buffer));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get the filename part of the given file
|
||||
// and remove the extension.
|
||||
// basename uses statically allocated memory that gets overwritten by each call.
|
||||
// but it returns a modifiable char*
|
||||
// so we might as well just edit that buffer directly because no-one will depend on this value later.
|
||||
filename = basename(argv[1]);
|
||||
|
||||
printf("Compressing %s...", filename);
|
||||
|
||||
bits_written = encodeZX0(output_buffer, input_buffer, input_buffer_size);
|
||||
delete[] input_buffer;
|
||||
input_buffer = nullptr;
|
||||
num_bytes = (bits_written + 7) / 8;
|
||||
|
||||
printf("done\n");
|
||||
|
||||
// if we have an extension in the filename, just end the string at the '.' position.
|
||||
extension_dot = strchr(filename, '.');
|
||||
if(extension_dot)
|
||||
{
|
||||
*extension_dot = '\0';
|
||||
}
|
||||
|
||||
// argv[2] should be the output directory
|
||||
snprintf(output_path, sizeof(output_path), "%s/%s_zx0.bin", argv[2], filename);
|
||||
|
||||
f = fopen(output_path, "wb+");
|
||||
fwrite(output_buffer, 1, num_bytes, f);
|
||||
fclose(f);
|
||||
|
||||
compress_ratio = static_cast<double>(num_bytes) / input_buffer_size;
|
||||
printf("Compressed size: %zu bytes/%zu bytes, Compression ratio: %f%%\n", num_bytes, input_buffer_size, compress_ratio * 100.f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
43
tools/data-generator/Makefile
Normal file
43
tools/data-generator/Makefile
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# # Compiler flags
|
||||
CXXFLAGS := -std=c++11 -fno-rtti -fno-exceptions -fno-unwind-tables -Wall -Wextra -I $(CURDIR)/include -g
|
||||
|
||||
# Source files directory
|
||||
SRC_DIR := ./src
|
||||
# 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))
|
||||
|
||||
# Ensure necessary directories exist
|
||||
# This function ensures the directory for the target exists
|
||||
define make_directory
|
||||
@mkdir -p $(dir $@)
|
||||
endef
|
||||
|
||||
# Target executable
|
||||
TARGET := data-generator
|
||||
|
||||
# Phony targets
|
||||
.PHONY: all clean
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) $^ -o $@
|
||||
|
||||
# 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) *.bin
|
||||
9
tools/data-generator/include/common.h
Normal file
9
tools/data-generator/include/common.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
void writeTable(const char* filename, const uint8_t *buffer, size_t buffer_size);
|
||||
|
||||
#endif
|
||||
6
tools/data-generator/include/pokemon_data.h
Normal file
6
tools/data-generator/include/pokemon_data.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef _POKEMON_DATA_H
|
||||
#define _POKEMON_DATA_H
|
||||
|
||||
void generate_pokemon_data();
|
||||
|
||||
#endif
|
||||
12
tools/data-generator/src/common.cpp
Normal file
12
tools/data-generator/src/common.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#include "common.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
void writeTable(const char* filename, const uint8_t *buffer, size_t buffer_size)
|
||||
{
|
||||
FILE* f;
|
||||
|
||||
f = fopen(filename, "wb+");
|
||||
fwrite(buffer, 1, buffer_size, f);
|
||||
fclose(f);
|
||||
}
|
||||
11
tools/data-generator/src/main.cpp
Normal file
11
tools/data-generator/src/main.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "pokemon_data.h"
|
||||
|
||||
// This application holds the various long static data arrays that Poke Transporter GB uses
|
||||
// and it writes them to .bin files that can be compressed with compressZX0 later.
|
||||
// it's useful to do it this way because it keeps this data easy to view, edit and document
|
||||
|
||||
int main(int /*argc*/, char **/*argv*/)
|
||||
{
|
||||
generate_pokemon_data();
|
||||
return 0;
|
||||
}
|
||||
5420
tools/data-generator/src/pokemon_data.cpp
Normal file
5420
tools/data-generator/src/pokemon_data.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user