mirror of https://github.com/zeldaret/tmc.git
Merge remote-tracking branch 'upstream/master' into sub_080809D4
This commit is contained in:
commit
f8f137cdd1
55
Makefile
55
Makefile
|
@ -82,14 +82,14 @@ LDFLAGS = -Map ../../$(MAP)
|
||||||
LIB := -L ../../tools/agbcc/lib -lc
|
LIB := -L ../../tools/agbcc/lib -lc
|
||||||
|
|
||||||
SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c
|
SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c
|
||||||
GFX := tools/gbagfx/gbagfx
|
GFX := tools/bin/gbagfx
|
||||||
AIF := tools/aif2pcm/aif2pcm
|
AIF := tools/bin/aif2pcm
|
||||||
MID := tools/mid2agb/mid2agb
|
MID := tools/bin/mid2agb
|
||||||
SCANINC := tools/scaninc/scaninc
|
SCANINC := tools/bin/scaninc
|
||||||
# TODO: use charmap?
|
# TODO: use charmap?
|
||||||
PREPROC := tools/preproc/preproc
|
PREPROC := tools/bin/preproc
|
||||||
FIX := tools/gbafix/gbafix
|
FIX := tools/bin/gbafix
|
||||||
ASSET_PROCESSOR := tools/asset_processor/asset_processor
|
ASSET_PROCESSOR := tools/bin/asset_processor
|
||||||
|
|
||||||
ASSET_CONFIGS = assets/assets.json assets/gfx.json assets/map.json assets/samples.json assets/sounds.json
|
ASSET_CONFIGS = assets/assets.json assets/gfx.json assets/map.json assets/samples.json assets/sounds.json
|
||||||
TRANSLATIONS = translations/USA.bin translations/English.bin translations/French.bin translations/German.bin translations/Spanish.bin translations/Italian.bin
|
TRANSLATIONS = translations/USA.bin translations/English.bin translations/French.bin translations/German.bin translations/Spanish.bin translations/Italian.bin
|
||||||
|
@ -149,11 +149,7 @@ SUBDIRS := $(sort $(dir $(OBJS)))
|
||||||
|
|
||||||
$(shell mkdir -p $(SUBDIRS))
|
$(shell mkdir -p $(SUBDIRS))
|
||||||
|
|
||||||
TOOLDIRS := $(filter-out tools/agbcc tools/binutils,$(wildcard tools/*))
|
.PHONY: all setup clean-tools mostlyclean clean tidy tools extractassets buildassets custom
|
||||||
TOOLBASE = $(TOOLDIRS:tools/%=%)
|
|
||||||
TOOLS = $(foreach tool,$(TOOLBASE),tools/$(tool)/$(tool)$(EXE))
|
|
||||||
|
|
||||||
.PHONY: all setup clean-tools mostlyclean clean tidy $(TOOLDIRS) extractassets
|
|
||||||
|
|
||||||
MAKEFLAGS += --no-print-directory
|
MAKEFLAGS += --no-print-directory
|
||||||
|
|
||||||
|
@ -166,11 +162,24 @@ all: build/extracted_assets_$(GAME_VERSION)
|
||||||
target: $(ROM)
|
target: $(ROM)
|
||||||
@$(SHA1) $(BUILD_NAME).sha1
|
@$(SHA1) $(BUILD_NAME).sha1
|
||||||
|
|
||||||
|
custom: buildassets
|
||||||
|
@$(MAKE) target GAME_VERSION=$(GAME_VERSION)
|
||||||
|
|
||||||
# kept for backwards compat
|
# kept for backwards compat
|
||||||
compare: $(ROM)
|
compare: $(ROM)
|
||||||
@$(SHA1) $(BUILD_NAME).sha1
|
@$(SHA1) $(BUILD_NAME).sha1
|
||||||
|
|
||||||
setup: $(TOOLDIRS)
|
setup: tools
|
||||||
|
|
||||||
|
# all tools are build at once
|
||||||
|
# FIXME figure out why make builds multiple times when specifying all tools here
|
||||||
|
tools: $(GFX)
|
||||||
|
|
||||||
|
$(GFX) $(AIF) $(MID) $(SCANINC) $(PREPROC) $(FIX) $(ASSET_PROCESSOR) tools/bin/agb2mid tools/bin/tmc_strings tools/bin/bin2c &:
|
||||||
|
mkdir -p tools/cmake-build
|
||||||
|
unset CC CXX AS LD LDFLAGS && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=tools -S tools -B tools/cmake-build
|
||||||
|
cmake --build tools/cmake-build -j
|
||||||
|
cmake --install tools/cmake-build
|
||||||
|
|
||||||
# Automatically extract binary data
|
# Automatically extract binary data
|
||||||
build/extracted_assets_%: $(ASSET_CONFIGS) $(TRANSLATIONS)
|
build/extracted_assets_%: $(ASSET_CONFIGS) $(TRANSLATIONS)
|
||||||
|
@ -181,8 +190,9 @@ build/extracted_assets_%: $(ASSET_CONFIGS) $(TRANSLATIONS)
|
||||||
extractassets:
|
extractassets:
|
||||||
$(ASSET_PROCESSOR) convert $(GAME_VERSION) $(ASSET_BUILDDIR)
|
$(ASSET_PROCESSOR) convert $(GAME_VERSION) $(ASSET_BUILDDIR)
|
||||||
|
|
||||||
$(TOOLDIRS):
|
# Build the assets from the human readable form
|
||||||
@$(MAKE) -C $@
|
buildassets:
|
||||||
|
$(ASSET_PROCESSOR) build $(GAME_VERSION) $(ASSET_BUILDDIR)
|
||||||
|
|
||||||
mostlyclean: tidy
|
mostlyclean: tidy
|
||||||
rm -f sound/direct_sound_samples/*.bin
|
rm -f sound/direct_sound_samples/*.bin
|
||||||
|
@ -191,7 +201,8 @@ mostlyclean: tidy
|
||||||
rm -f $(AUTO_GEN_TARGETS)
|
rm -f $(AUTO_GEN_TARGETS)
|
||||||
|
|
||||||
clean-tools:
|
clean-tools:
|
||||||
@$(foreach tooldir,$(TOOLDIRS),$(MAKE) clean -C $(tooldir);)
|
rm -rf tools/bin
|
||||||
|
rm -rf tools/cmake-build
|
||||||
|
|
||||||
clean: mostlyclean clean-tools
|
clean: mostlyclean clean-tools
|
||||||
|
|
||||||
|
@ -217,12 +228,12 @@ tidy:
|
||||||
%.lz: % ; $(GFX) $< $@
|
%.lz: % ; $(GFX) $< $@
|
||||||
%.rl: % ; $(GFX) $< $@
|
%.rl: % ; $(GFX) $< $@
|
||||||
cd $(@D) && ../../$(MID) $(<F)
|
cd $(@D) && ../../$(MID) $(<F)
|
||||||
translations/USA.bin: translations/USA.json ; tools/tmc_strings/tmc_strings -p --source $< --dest $@ --size 0x499E0
|
translations/USA.bin: translations/USA.json ; tools/bin/tmc_strings -p --source $< --dest $@ --size 0x499E0
|
||||||
translations/English.bin: translations/English.json ; tools/tmc_strings/tmc_strings -p --source $< --dest $@ --size 0x488C0
|
translations/English.bin: translations/English.json ; tools/bin/tmc_strings -p --source $< --dest $@ --size 0x488C0
|
||||||
translations/French.bin: translations/French.json ; tools/tmc_strings/tmc_strings -p --source $< --dest $@ --size 0x47A90
|
translations/French.bin: translations/French.json ; tools/bin/tmc_strings -p --source $< --dest $@ --size 0x47A90
|
||||||
translations/German.bin: translations/German.json ; tools/tmc_strings/tmc_strings -p --source $< --dest $@ --size 0x42FC0
|
translations/German.bin: translations/German.json ; tools/bin/tmc_strings -p --source $< --dest $@ --size 0x42FC0
|
||||||
translations/Spanish.bin: translations/Spanish.json ; tools/tmc_strings/tmc_strings -p --source $< --dest $@ --size 0x41930
|
translations/Spanish.bin: translations/Spanish.json ; tools/bin/tmc_strings -p --source $< --dest $@ --size 0x41930
|
||||||
translations/Italian.bin: translations/Italian.json ; tools/tmc_strings/tmc_strings -p --source $< --dest $@ --size 0x438E0
|
translations/Italian.bin: translations/Italian.json ; tools/bin/tmc_strings -p --source $< --dest $@ --size 0x438E0
|
||||||
|
|
||||||
ifeq ($(NODEP),1)
|
ifeq ($(NODEP),1)
|
||||||
$(C_BUILDDIR)/%.o: c_dep :=
|
$(C_BUILDDIR)/%.o: c_dep :=
|
||||||
|
|
|
@ -51464,28 +51464,19 @@
|
||||||
"path": "sprites/gSprite_Link.4bpp",
|
"path": "sprites/gSprite_Link.4bpp",
|
||||||
"start": 1289748,
|
"start": 1289748,
|
||||||
"size": 581216,
|
"size": 581216,
|
||||||
"type": "gfx",
|
"type": "gfx"
|
||||||
"options": {
|
|
||||||
"width": 4
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "sprites/gSprite_081C8C74.4bpp",
|
"path": "sprites/gSprite_081C8C74.4bpp",
|
||||||
"start": 1870964,
|
"start": 1870964,
|
||||||
"size": 59904,
|
"size": 59904,
|
||||||
"type": "gfx",
|
"type": "gfx"
|
||||||
"options": {
|
|
||||||
"width": 4
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "sprites/gSprite_081D7674.4bpp",
|
"path": "sprites/gSprite_081D7674.4bpp",
|
||||||
"start": 1930868,
|
"start": 1930868,
|
||||||
"size": 33216,
|
"size": 33216,
|
||||||
"type": "gfx",
|
"type": "gfx"
|
||||||
"options": {
|
|
||||||
"width": 4
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "sprites/gSprite_081DF834/unk_0.4bpp",
|
"path": "sprites/gSprite_081DF834/unk_0.4bpp",
|
||||||
|
@ -51729,18 +51720,18 @@
|
||||||
{
|
{
|
||||||
"path": "sprites/gSprite_081FF1B4/unk_3_1.4bpp",
|
"path": "sprites/gSprite_081FF1B4/unk_3_1.4bpp",
|
||||||
"start": 2243508,
|
"start": 2243508,
|
||||||
"size": 4986,
|
"size": 4960,
|
||||||
"type": "gfx"
|
"type": "gfx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "sprites/gSprite_081FF1B4/config_1.txt",
|
"path": "sprites/gSprite_081FF1B4/config_1.txt",
|
||||||
"start": 2248494,
|
"start": 2248468,
|
||||||
"size": 1024
|
"size": 1024
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "sprites/gSprite_081FF1B4/unk_3_2.4bpp",
|
"path": "sprites/gSprite_081FF1B4/unk_3_2.4bpp",
|
||||||
"start": 2249518,
|
"start": 2249492,
|
||||||
"size": 4198,
|
"size": 4224,
|
||||||
"type": "gfx"
|
"type": "gfx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -52229,116 +52220,6 @@
|
||||||
"JP": -864
|
"JP": -864
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086926A0.bin",
|
|
||||||
"start": 6891168,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692780.bin",
|
|
||||||
"start": 6891392,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692860.bin",
|
|
||||||
"start": 6891616,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692940.bin",
|
|
||||||
"start": 6891840,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692A20.bin",
|
|
||||||
"start": 6892064,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692B00.bin",
|
|
||||||
"start": 6892288,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692BE0.bin",
|
|
||||||
"start": 6892512,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692CC0.bin",
|
|
||||||
"start": 6892736,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692DA0.bin",
|
|
||||||
"start": 6892960,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692E80.bin",
|
|
||||||
"start": 6893184,
|
|
||||||
"size": 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692F60.bin",
|
|
||||||
"start": 6893408,
|
|
||||||
"size": 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08692F60_1.bin",
|
|
||||||
"start": 6893472,
|
|
||||||
"size": 16320
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_08696F60.bin",
|
|
||||||
"start": 6909792,
|
|
||||||
"size": 2432
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086978E0.bin",
|
|
||||||
"start": 6912224,
|
|
||||||
"size": 8192
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086998E0.bin",
|
|
||||||
"start": 6920416,
|
|
||||||
"size": 32768
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086A18E0.bin",
|
|
||||||
"start": 6953184,
|
|
||||||
"size": 4448
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086A18E0_1.bin",
|
|
||||||
"start": 6957632,
|
|
||||||
"size": 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086A2A60.bin",
|
|
||||||
"start": 6957664,
|
|
||||||
"size": 1152
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086A2EE0.bin",
|
|
||||||
"start": 6958816,
|
|
||||||
"size": 32768
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086AAEE0.bin",
|
|
||||||
"start": 6991584,
|
|
||||||
"size": 169344
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086D4460.bin",
|
|
||||||
"start": 7160928,
|
|
||||||
"size": 81920
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "data_08132B30/gUnk_086E8460.bin",
|
|
||||||
"start": 7242848,
|
|
||||||
"size": 161088
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "strings/translation_DEMO_JP.bin",
|
"path": "strings/translation_DEMO_JP.bin",
|
||||||
"variants": [
|
"variants": [
|
||||||
|
|
116
assets/gfx.json
116
assets/gfx.json
|
@ -16484,6 +16484,116 @@
|
||||||
"size": 12288,
|
"size": 12288,
|
||||||
"type": "gfx"
|
"type": "gfx"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086926A0.bin",
|
||||||
|
"start": 6891168,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692780.bin",
|
||||||
|
"start": 6891392,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692860.bin",
|
||||||
|
"start": 6891616,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692940.bin",
|
||||||
|
"start": 6891840,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692A20.bin",
|
||||||
|
"start": 6892064,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692B00.bin",
|
||||||
|
"start": 6892288,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692BE0.bin",
|
||||||
|
"start": 6892512,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692CC0.bin",
|
||||||
|
"start": 6892736,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692DA0.bin",
|
||||||
|
"start": 6892960,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692E80.bin",
|
||||||
|
"start": 6893184,
|
||||||
|
"size": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692F60.bin",
|
||||||
|
"start": 6893408,
|
||||||
|
"size": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08692F60_1.bin",
|
||||||
|
"start": 6893472,
|
||||||
|
"size": 16320
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_08696F60.bin",
|
||||||
|
"start": 6909792,
|
||||||
|
"size": 2432
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086978E0.bin",
|
||||||
|
"start": 6912224,
|
||||||
|
"size": 8192
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086998E0.bin",
|
||||||
|
"start": 6920416,
|
||||||
|
"size": 32768
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086A18E0.bin",
|
||||||
|
"start": 6953184,
|
||||||
|
"size": 4448
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086A18E0_1.bin",
|
||||||
|
"start": 6957632,
|
||||||
|
"size": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086A2A60.bin",
|
||||||
|
"start": 6957664,
|
||||||
|
"size": 1152
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086A2EE0.bin",
|
||||||
|
"start": 6958816,
|
||||||
|
"size": 32768
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086AAEE0.bin",
|
||||||
|
"start": 6991584,
|
||||||
|
"size": 169344
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086D4460.bin",
|
||||||
|
"start": 7160928,
|
||||||
|
"size": 81920
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "gfx/gUnk_086E8460.bin",
|
||||||
|
"start": 7242848,
|
||||||
|
"size": 161088
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "gfx/gGfx_38_0.4bpp",
|
"path": "gfx/gGfx_38_0.4bpp",
|
||||||
"start": 7403936,
|
"start": 7403936,
|
||||||
|
@ -19326,13 +19436,13 @@
|
||||||
{
|
{
|
||||||
"path": "gfx/fixedTypeGfx_214.4bpp.lz",
|
"path": "gfx/fixedTypeGfx_214.4bpp.lz",
|
||||||
"start": 10163804,
|
"start": 10163804,
|
||||||
"size": 1024,
|
"size": 628,
|
||||||
"type": "gfx"
|
"type": "gfx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/gfx_unknown_49.bin",
|
"path": "assets/gfx_unknown_49.bin",
|
||||||
"start": 10164828,
|
"start": 10164432,
|
||||||
"size": 820,
|
"size": 1216,
|
||||||
"type": "unknown"
|
"type": "unknown"
|
||||||
}
|
}
|
||||||
]
|
]
|
696
assets/map.json
696
assets/map.json
File diff suppressed because it is too large
Load Diff
|
@ -5487,69 +5487,68 @@ fixedTypeGfx_197::
|
||||||
fixedTypeGfx_198::
|
fixedTypeGfx_198::
|
||||||
.incbin "gfx/fixedTypeGfx_198.4bpp"
|
.incbin "gfx/fixedTypeGfx_198.4bpp"
|
||||||
gfx_unknown_20::
|
gfx_unknown_20::
|
||||||
@ TODO move those imports to gfx.json and assets/folder?
|
|
||||||
gUnk_086926A0:: @ 086926A0
|
gUnk_086926A0:: @ 086926A0
|
||||||
.incbin "data_08132B30/gUnk_086926A0.bin"
|
.incbin "gfx/gUnk_086926A0.bin"
|
||||||
|
|
||||||
gUnk_08692780:: @ 08692780
|
gUnk_08692780:: @ 08692780
|
||||||
.incbin "data_08132B30/gUnk_08692780.bin"
|
.incbin "gfx/gUnk_08692780.bin"
|
||||||
|
|
||||||
gUnk_08692860:: @ 08692860
|
gUnk_08692860:: @ 08692860
|
||||||
.incbin "data_08132B30/gUnk_08692860.bin"
|
.incbin "gfx/gUnk_08692860.bin"
|
||||||
|
|
||||||
gUnk_08692940:: @ 08692940
|
gUnk_08692940:: @ 08692940
|
||||||
.incbin "data_08132B30/gUnk_08692940.bin"
|
.incbin "gfx/gUnk_08692940.bin"
|
||||||
|
|
||||||
gUnk_08692A20:: @ 08692A20
|
gUnk_08692A20:: @ 08692A20
|
||||||
.incbin "data_08132B30/gUnk_08692A20.bin"
|
.incbin "gfx/gUnk_08692A20.bin"
|
||||||
|
|
||||||
gUnk_08692B00:: @ 08692B00
|
gUnk_08692B00:: @ 08692B00
|
||||||
.incbin "data_08132B30/gUnk_08692B00.bin"
|
.incbin "gfx/gUnk_08692B00.bin"
|
||||||
|
|
||||||
gUnk_08692BE0:: @ 08692BE0
|
gUnk_08692BE0:: @ 08692BE0
|
||||||
.incbin "data_08132B30/gUnk_08692BE0.bin"
|
.incbin "gfx/gUnk_08692BE0.bin"
|
||||||
|
|
||||||
gUnk_08692CC0:: @ 08692CC0
|
gUnk_08692CC0:: @ 08692CC0
|
||||||
.incbin "data_08132B30/gUnk_08692CC0.bin"
|
.incbin "gfx/gUnk_08692CC0.bin"
|
||||||
|
|
||||||
gUnk_08692DA0:: @ 08692DA0
|
gUnk_08692DA0:: @ 08692DA0
|
||||||
.incbin "data_08132B30/gUnk_08692DA0.bin"
|
.incbin "gfx/gUnk_08692DA0.bin"
|
||||||
|
|
||||||
gUnk_08692E80:: @ 08692E80
|
gUnk_08692E80:: @ 08692E80
|
||||||
.incbin "data_08132B30/gUnk_08692E80.bin"
|
.incbin "gfx/gUnk_08692E80.bin"
|
||||||
|
|
||||||
gUnk_08692F60:: @ 08692F60
|
gUnk_08692F60:: @ 08692F60
|
||||||
.incbin "data_08132B30/gUnk_08692F60.bin"
|
.incbin "gfx/gUnk_08692F60.bin"
|
||||||
@ TODO 08692FA0 to 086A2A3F is .incbin "graphics/font.4bpp"
|
@ TODO 08692FA0 to 086A2A3F is .incbin "graphics/font.4bpp"
|
||||||
.incbin "data_08132B30/gUnk_08692F60_1.bin"
|
.incbin "gfx/gUnk_08692F60_1.bin"
|
||||||
gUnk_08696F60:: @ 08696F60
|
gUnk_08696F60:: @ 08696F60
|
||||||
.incbin "data_08132B30/gUnk_08696F60.bin"
|
.incbin "gfx/gUnk_08696F60.bin"
|
||||||
gUnk_086978E0:: @ 086978E0
|
gUnk_086978E0:: @ 086978E0
|
||||||
.incbin "data_08132B30/gUnk_086978E0.bin"
|
.incbin "gfx/gUnk_086978E0.bin"
|
||||||
gUnk_086998E0:: @ 086998E0
|
gUnk_086998E0:: @ 086998E0
|
||||||
.incbin "data_08132B30/gUnk_086998E0.bin"
|
.incbin "gfx/gUnk_086998E0.bin"
|
||||||
gUnk_086A18E0:: @ 086A18E0
|
gUnk_086A18E0:: @ 086A18E0
|
||||||
.incbin "data_08132B30/gUnk_086A18E0.bin"
|
.incbin "gfx/gUnk_086A18E0.bin"
|
||||||
|
|
||||||
.incbin "data_08132B30/gUnk_086A18E0_1.bin"
|
.incbin "gfx/gUnk_086A18E0_1.bin"
|
||||||
gUnk_086A2A60:: @ 086A2A60
|
gUnk_086A2A60:: @ 086A2A60
|
||||||
.incbin "data_08132B30/gUnk_086A2A60.bin"
|
.incbin "gfx/gUnk_086A2A60.bin"
|
||||||
|
|
||||||
gUnk_086A2EE0:: @ 086A2EE0
|
gUnk_086A2EE0:: @ 086A2EE0
|
||||||
.incbin "data_08132B30/gUnk_086A2EE0.bin"
|
.incbin "gfx/gUnk_086A2EE0.bin"
|
||||||
|
|
||||||
gUnk_086AAEE0:: @ 086AAEE0
|
gUnk_086AAEE0:: @ 086AAEE0
|
||||||
.incbin "data_08132B30/gUnk_086AAEE0.bin"
|
.incbin "gfx/gUnk_086AAEE0.bin"
|
||||||
|
|
||||||
@ TODO background graphics?
|
@ TODO background graphics?
|
||||||
@ TODO check http://kylehalladay.com/blog/tutorial/gba/2017/04/11/GBA-By-Example-3.html
|
@ TODO check http://kylehalladay.com/blog/tutorial/gba/2017/04/11/GBA-By-Example-3.html
|
||||||
@ TODO contains credits images?
|
@ TODO contains credits images?
|
||||||
gUnk_086D4460:: @ 086D4460
|
gUnk_086D4460:: @ 086D4460
|
||||||
.incbin "data_08132B30/gUnk_086D4460.bin"
|
.incbin "gfx/gUnk_086D4460.bin"
|
||||||
|
|
||||||
@ TODO 0x800 of this might be backgrond?
|
@ TODO 0x800 of this might be background?
|
||||||
gUnk_086E8460:: @ 086E8460
|
gUnk_086E8460:: @ 086E8460
|
||||||
.incbin "data_08132B30/gUnk_086E8460.bin"
|
.incbin "gfx/gUnk_086E8460.bin"
|
||||||
gGfx_38_0::
|
gGfx_38_0::
|
||||||
.incbin "gfx/gGfx_38_0.4bpp"
|
.incbin "gfx/gGfx_38_0.4bpp"
|
||||||
gGfx_36_0::
|
gGfx_36_0::
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
||||||
|
bin
|
||||||
|
cmake-build*
|
|
@ -0,0 +1,43 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(tools)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
add_library(project_settings INTERFACE)
|
||||||
|
|
||||||
|
# setup compiler warnings
|
||||||
|
file(DOWNLOAD https://raw.githubusercontent.com/cpp-best-practices/cpp_starter_project/master/cmake/CompilerWarnings.cmake ${CMAKE_BINARY_DIR}/CompilerWarnings.cmake)
|
||||||
|
include(${CMAKE_BINARY_DIR}/CompilerWarnings.cmake)
|
||||||
|
set_project_warnings(project_settings)
|
||||||
|
|
||||||
|
# nlohmann/json
|
||||||
|
# this repo is a mirror, that only holds the release versions of the headers to keep the size small
|
||||||
|
FetchContent_Declare(
|
||||||
|
json
|
||||||
|
GIT_REPOSITORY https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent
|
||||||
|
GIT_TAG v3.10.4
|
||||||
|
)
|
||||||
|
# {fmt}
|
||||||
|
FetchContent_Declare(
|
||||||
|
fmt
|
||||||
|
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
|
||||||
|
GIT_TAG 8.0.1
|
||||||
|
)
|
||||||
|
# CLI11
|
||||||
|
FetchContent_Declare(
|
||||||
|
cli11
|
||||||
|
GIT_REPOSITORY https://github.com/CLIUtils/CLI11
|
||||||
|
GIT_TAG v2.1.2
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(json fmt cli11)
|
||||||
|
|
||||||
|
add_library(filesystem INTERFACE)
|
||||||
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
||||||
|
if (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 9.0.0)
|
||||||
|
target_link_libraries(filesystem INTERFACE stdc++fs)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
|
@ -1 +0,0 @@
|
||||||
agb2mid
|
|
|
@ -1,18 +0,0 @@
|
||||||
CXX := g++
|
|
||||||
|
|
||||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror -g
|
|
||||||
|
|
||||||
SRCS := agb.cpp error.cpp main.cpp midi.cpp tables.cpp
|
|
||||||
|
|
||||||
HEADERS := agb.h error.h main.h midi.h tables.h
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: agb2mid
|
|
||||||
@:
|
|
||||||
|
|
||||||
agb2mid: $(SRCS) $(HEADERS)
|
|
||||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) agb2mid agb2mid.exe
|
|
|
@ -1,2 +0,0 @@
|
||||||
aif2pcm
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
CC = gcc
|
|
||||||
|
|
||||||
CFLAGS = -Wall -Wextra -Wno-switch -Werror -std=c11 -O2
|
|
||||||
|
|
||||||
LIBS = -lm
|
|
||||||
|
|
||||||
SRCS = main.c extended.c
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: aif2pcm
|
|
||||||
@:
|
|
||||||
|
|
||||||
aif2pcm: $(SRCS)
|
|
||||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) aif2pcm aif2pcm.exe
|
|
|
@ -1,172 +0,0 @@
|
||||||
/* $Id: extended.c,v 1.8 2006/12/23 11:17:49 toad32767 Exp $ */
|
|
||||||
/*-
|
|
||||||
* Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any
|
|
||||||
* person obtaining a copy of this software and associated
|
|
||||||
* documentation files (the "Software"), to deal in the
|
|
||||||
* Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the
|
|
||||||
* Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice
|
|
||||||
* shall be included in all copies or substantial portions of
|
|
||||||
* the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
|
||||||
* KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
||||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
|
||||||
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Infinite & NAN values
|
|
||||||
* for non-IEEE systems
|
|
||||||
*/
|
|
||||||
#ifndef HUGE_VAL
|
|
||||||
#ifdef HUGE
|
|
||||||
#define INFINITE_VALUE HUGE
|
|
||||||
#define NAN_VALUE HUGE
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define INFINITE_VALUE HUGE_VAL
|
|
||||||
#define NAN_VALUE HUGE_VAL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IEEE 754 Extended Precision
|
|
||||||
*
|
|
||||||
* Implementation here is the 80-bit extended precision
|
|
||||||
* format of Motorola 68881, Motorola 68882 and Motorola
|
|
||||||
* 68040 FPUs, as well as Intel 80x87 FPUs.
|
|
||||||
*
|
|
||||||
* See:
|
|
||||||
* http://www.freescale.com/files/32bit/doc/fact_sheet/BR509.pdf
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Exponent range: [-16383,16383]
|
|
||||||
* Precision for mantissa: 64 bits with no hidden bit
|
|
||||||
* Bias: 16383
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write IEEE Extended Precision Numbers
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ieee754_write_extended(double in, uint8_t* out)
|
|
||||||
{
|
|
||||||
int sgn, exp, shift;
|
|
||||||
double fraction, t;
|
|
||||||
unsigned int lexp, hexp;
|
|
||||||
unsigned long low, high;
|
|
||||||
|
|
||||||
if (in == 0.0) {
|
|
||||||
memset(out, 0, 10);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (in < 0.0) {
|
|
||||||
in = fabs(in);
|
|
||||||
sgn = 1;
|
|
||||||
} else
|
|
||||||
sgn = 0;
|
|
||||||
|
|
||||||
fraction = frexp(in, &exp);
|
|
||||||
|
|
||||||
if (exp == 0 || exp > 16384) {
|
|
||||||
if (exp > 16384) /* infinite value */
|
|
||||||
low = high = 0;
|
|
||||||
else {
|
|
||||||
low = 0x80000000;
|
|
||||||
high = 0;
|
|
||||||
}
|
|
||||||
exp = 32767;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
fraction = ldexp(fraction, 32);
|
|
||||||
t = floor(fraction);
|
|
||||||
low = (unsigned long) t;
|
|
||||||
fraction -= t;
|
|
||||||
t = floor(ldexp(fraction, 32));
|
|
||||||
high = (unsigned long) t;
|
|
||||||
|
|
||||||
/* Convert exponents < -16382 to -16382 (then they will be
|
|
||||||
* stored as -16383) */
|
|
||||||
if (exp < -16382) {
|
|
||||||
shift = 0 - exp - 16382;
|
|
||||||
high >>= shift;
|
|
||||||
high |= (low << (32 - shift));
|
|
||||||
low >>= shift;
|
|
||||||
exp = -16382;
|
|
||||||
}
|
|
||||||
exp += 16383 - 1; /* bias */
|
|
||||||
|
|
||||||
done:
|
|
||||||
lexp = ((unsigned int) exp) >> 8;
|
|
||||||
hexp = ((unsigned int) exp) & 0xFF;
|
|
||||||
|
|
||||||
/* big endian */
|
|
||||||
out[0] = ((uint8_t) sgn) << 7;
|
|
||||||
out[0] |= (uint8_t) lexp;
|
|
||||||
out[1] = (uint8_t) hexp;
|
|
||||||
out[2] = (uint8_t) (low >> 24);
|
|
||||||
out[3] = (uint8_t) ((low >> 16) & 0xFF);
|
|
||||||
out[4] = (uint8_t) ((low >> 8) & 0xFF);
|
|
||||||
out[5] = (uint8_t) (low & 0xFF);
|
|
||||||
out[6] = (uint8_t) (high >> 24);
|
|
||||||
out[7] = (uint8_t) ((high >> 16) & 0xFF);
|
|
||||||
out[8] = (uint8_t) ((high >> 8) & 0xFF);
|
|
||||||
out[9] = (uint8_t) (high & 0xFF);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read IEEE Extended Precision Numbers
|
|
||||||
*/
|
|
||||||
double
|
|
||||||
ieee754_read_extended(uint8_t* in)
|
|
||||||
{
|
|
||||||
int sgn, exp;
|
|
||||||
unsigned long low, high;
|
|
||||||
double out;
|
|
||||||
|
|
||||||
/* Extract the components from the big endian buffer */
|
|
||||||
sgn = (int) (in[0] >> 7);
|
|
||||||
exp = ((int) (in[0] & 0x7F) << 8) | ((int) in[1]);
|
|
||||||
low = (((unsigned long) in[2]) << 24)
|
|
||||||
| (((unsigned long) in[3]) << 16)
|
|
||||||
| (((unsigned long) in[4]) << 8) | (unsigned long) in[5];
|
|
||||||
high = (((unsigned long) in[6]) << 24)
|
|
||||||
| (((unsigned long) in[7]) << 16)
|
|
||||||
| (((unsigned long) in[8]) << 8) | (unsigned long) in[9];
|
|
||||||
|
|
||||||
if (exp == 0 && low == 0 && high == 0)
|
|
||||||
return (sgn ? -0.0 : 0.0);
|
|
||||||
|
|
||||||
switch (exp) {
|
|
||||||
case 32767:
|
|
||||||
if (low == 0 && high == 0)
|
|
||||||
return (sgn ? -INFINITE_VALUE : INFINITE_VALUE);
|
|
||||||
else
|
|
||||||
return (sgn ? -NAN_VALUE : NAN_VALUE);
|
|
||||||
default:
|
|
||||||
exp -= 16383; /* unbias exponent */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
out = ldexp((double) low, -31 + exp);
|
|
||||||
out += ldexp((double) high, -63 + exp);
|
|
||||||
|
|
||||||
return (sgn ? -out : out);
|
|
||||||
}
|
|
|
@ -1,888 +0,0 @@
|
||||||
// Copyright(c) 2016 huderlem
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
/* extended.c */
|
|
||||||
void ieee754_write_extended (double, uint8_t*);
|
|
||||||
double ieee754_read_extended (uint8_t*);
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
|
|
||||||
#define FATAL_ERROR(format, ...) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
fprintf(stderr, format, __VA_ARGS__); \
|
|
||||||
exit(1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define FATAL_ERROR(format, ...) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
fprintf(stderr, format, ##__VA_ARGS__); \
|
|
||||||
exit(1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned long num_samples;
|
|
||||||
uint8_t *samples;
|
|
||||||
uint8_t midi_note;
|
|
||||||
bool has_loop;
|
|
||||||
unsigned long loop_offset;
|
|
||||||
double sample_rate;
|
|
||||||
unsigned long real_num_samples;
|
|
||||||
} AifData;
|
|
||||||
|
|
||||||
struct Bytes {
|
|
||||||
unsigned long length;
|
|
||||||
uint8_t *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Marker {
|
|
||||||
unsigned short id;
|
|
||||||
unsigned long position;
|
|
||||||
// don't care about the name
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Bytes *read_bytearray(const char *filename)
|
|
||||||
{
|
|
||||||
struct Bytes *bytes = malloc(sizeof(struct Bytes));
|
|
||||||
FILE *f = fopen(filename, "rb");
|
|
||||||
if (!f)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("Failed to open '%s' for reading!\n", filename);
|
|
||||||
}
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
bytes->length = ftell(f);
|
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
bytes->data = malloc(bytes->length);
|
|
||||||
unsigned long read = fread(bytes->data, bytes->length, 1, f);
|
|
||||||
fclose(f);
|
|
||||||
if (read <= 0)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("Failed to read data from '%s'!\n", filename);
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_bytearray(const char *filename, struct Bytes *bytes)
|
|
||||||
{
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if (!f)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("Failed to open '%s' for writing!\n", filename);
|
|
||||||
}
|
|
||||||
fwrite(bytes->data, bytes->length, 1, f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_bytearray(struct Bytes *bytes)
|
|
||||||
{
|
|
||||||
free(bytes->data);
|
|
||||||
free(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *get_file_extension(char *filename)
|
|
||||||
{
|
|
||||||
char *index = strrchr(filename, '.');
|
|
||||||
if (!index || index == filename)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return index + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *new_file_extension(char *filename, char *ext)
|
|
||||||
{
|
|
||||||
char *index = strrchr(filename, '.');
|
|
||||||
if (!index || index == filename)
|
|
||||||
{
|
|
||||||
index = filename + strlen(filename);
|
|
||||||
}
|
|
||||||
int length = index - filename;
|
|
||||||
char *new_filename = malloc(length + 1 + strlen(ext) + 1);
|
|
||||||
if (new_filename)
|
|
||||||
{
|
|
||||||
strcpy(new_filename, filename);
|
|
||||||
new_filename[length] = '.';
|
|
||||||
strcpy(new_filename + length + 1, ext);
|
|
||||||
}
|
|
||||||
return new_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_aif(struct Bytes *aif, AifData *aif_data)
|
|
||||||
{
|
|
||||||
aif_data->has_loop = false;
|
|
||||||
aif_data->num_samples = 0;
|
|
||||||
|
|
||||||
unsigned long pos = 0;
|
|
||||||
char chunk_name[5]; chunk_name[4] = '\0';
|
|
||||||
char chunk_type[5]; chunk_type[4] = '\0';
|
|
||||||
|
|
||||||
// Check for FORM Chunk
|
|
||||||
memcpy(chunk_name, &aif->data[pos], 4);
|
|
||||||
pos += 4;
|
|
||||||
if (strcmp(chunk_name, "FORM") != 0)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("Input .aif file has invalid header Chunk '%s'!\n", chunk_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read size of whole file.
|
|
||||||
unsigned long whole_chunk_size = aif->data[pos++] << 24;
|
|
||||||
whole_chunk_size |= (aif->data[pos++] << 16);
|
|
||||||
whole_chunk_size |= (aif->data[pos++] << 8);
|
|
||||||
whole_chunk_size |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
unsigned long expected_whole_chunk_size = aif->length - 8;
|
|
||||||
if (whole_chunk_size != expected_whole_chunk_size)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("FORM Chunk ckSize '%lu' doesn't match actual size '%lu'!\n", whole_chunk_size, expected_whole_chunk_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for AIFF Form Type
|
|
||||||
memcpy(chunk_type, &aif->data[pos], 4);
|
|
||||||
pos += 4;
|
|
||||||
if (strcmp(chunk_type, "AIFF") != 0)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("FORM Type is '%s', but it must be AIFF!", chunk_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Marker *markers = NULL;
|
|
||||||
unsigned short num_markers = 0, loop_start = 0, loop_end = 0;
|
|
||||||
unsigned long num_sample_frames = 0;
|
|
||||||
|
|
||||||
// Read all the Chunks to populate the AifData struct.
|
|
||||||
while ((pos + 8) < aif->length)
|
|
||||||
{
|
|
||||||
// Read Chunk id
|
|
||||||
memcpy(chunk_name, &aif->data[pos], 4);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
unsigned long chunk_size = (aif->data[pos++] << 24);
|
|
||||||
chunk_size |= (aif->data[pos++] << 16);
|
|
||||||
chunk_size |= (aif->data[pos++] << 8);
|
|
||||||
chunk_size |= aif->data[pos++];
|
|
||||||
|
|
||||||
if ((pos + chunk_size) > aif->length)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("%s chunk at 0x%lx reached end of file before finishing\n", chunk_name, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(chunk_name, "COMM") == 0)
|
|
||||||
{
|
|
||||||
short num_channels = (aif->data[pos++] << 8);
|
|
||||||
num_channels |= (uint8_t)aif->data[pos++];
|
|
||||||
if (num_channels != 1)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("numChannels (%d) in the COMM Chunk must be 1!\n", num_channels);
|
|
||||||
}
|
|
||||||
|
|
||||||
num_sample_frames = (aif->data[pos++] << 24);
|
|
||||||
num_sample_frames |= (aif->data[pos++] << 16);
|
|
||||||
num_sample_frames |= (aif->data[pos++] << 8);
|
|
||||||
num_sample_frames |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
short sample_size = (aif->data[pos++] << 8);
|
|
||||||
sample_size |= (uint8_t)aif->data[pos++];
|
|
||||||
if (sample_size != 8)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("sampleSize (%d) in the COMM Chunk must be 8!\n", sample_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
double sample_rate = ieee754_read_extended((uint8_t*)(aif->data + pos));
|
|
||||||
pos += 10;
|
|
||||||
|
|
||||||
aif_data->sample_rate = sample_rate;
|
|
||||||
|
|
||||||
if (aif_data->num_samples == 0)
|
|
||||||
{
|
|
||||||
aif_data->num_samples = num_sample_frames;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp(chunk_name, "MARK") == 0)
|
|
||||||
{
|
|
||||||
num_markers = (aif->data[pos++] << 8);
|
|
||||||
num_markers |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
if (markers)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("More than one MARK Chunk in file!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
markers = calloc(num_markers, sizeof(struct Marker));
|
|
||||||
|
|
||||||
// Read each marker.
|
|
||||||
for (int i = 0; i < num_markers; i++)
|
|
||||||
{
|
|
||||||
unsigned short marker_id = (aif->data[pos++] << 8);
|
|
||||||
marker_id |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
unsigned long marker_position = (aif->data[pos++] << 24);
|
|
||||||
marker_position |= (aif->data[pos++] << 16);
|
|
||||||
marker_position |= (aif->data[pos++] << 8);
|
|
||||||
marker_position |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
// Marker name is a Pascal-style string.
|
|
||||||
uint8_t marker_name_size = aif->data[pos++];
|
|
||||||
// We don't actually need the marker name for anything anymore.
|
|
||||||
/*char *marker_name = (char *)malloc((marker_name_size + 1) * sizeof(char));
|
|
||||||
memcpy(marker_name, &aif->data[pos], marker_name_size);
|
|
||||||
marker_name[marker_name_size] = '\0';*/
|
|
||||||
pos += marker_name_size + !(marker_name_size & 1);
|
|
||||||
|
|
||||||
markers[i].id = marker_id;
|
|
||||||
markers[i].position = marker_position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp(chunk_name, "INST") == 0)
|
|
||||||
{
|
|
||||||
uint8_t midi_note = (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
aif_data->midi_note = midi_note;
|
|
||||||
|
|
||||||
// Skip over data we don't need.
|
|
||||||
pos += 7;
|
|
||||||
|
|
||||||
unsigned short loop_type = (aif->data[pos++] << 8);
|
|
||||||
loop_type |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
if (loop_type)
|
|
||||||
{
|
|
||||||
loop_start = (aif->data[pos++] << 8);
|
|
||||||
loop_start |= (uint8_t)aif->data[pos++];
|
|
||||||
|
|
||||||
loop_end = (aif->data[pos++] << 8);
|
|
||||||
loop_end |= (uint8_t)aif->data[pos++];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Skip NoLooping sustain loop.
|
|
||||||
pos += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip release loop, we don't need it.
|
|
||||||
pos += 6;
|
|
||||||
}
|
|
||||||
else if (strcmp(chunk_name, "SSND") == 0)
|
|
||||||
{
|
|
||||||
// Skip offset and blockSize
|
|
||||||
pos += 8;
|
|
||||||
|
|
||||||
unsigned long num_samples = chunk_size - 8;
|
|
||||||
uint8_t *sample_data = (uint8_t *)malloc(num_samples * sizeof(uint8_t));
|
|
||||||
memcpy(sample_data, &aif->data[pos], num_samples);
|
|
||||||
|
|
||||||
aif_data->samples = sample_data;
|
|
||||||
aif_data->real_num_samples = num_samples;
|
|
||||||
pos += chunk_size - 8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Skip over unsupported chunks.
|
|
||||||
pos += chunk_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (markers)
|
|
||||||
{
|
|
||||||
// Resolve loop points.
|
|
||||||
struct Marker *cur_marker = markers;
|
|
||||||
|
|
||||||
// Grab loop start point.
|
|
||||||
for (int i = 0; i < num_markers; i++, cur_marker++)
|
|
||||||
{
|
|
||||||
if (cur_marker->id == loop_start)
|
|
||||||
{
|
|
||||||
aif_data->loop_offset = cur_marker->position;
|
|
||||||
aif_data->has_loop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_marker = markers;
|
|
||||||
|
|
||||||
// Grab loop end point.
|
|
||||||
for (int i = 0; i < num_markers; i++, cur_marker++)
|
|
||||||
{
|
|
||||||
if (cur_marker->id == loop_end)
|
|
||||||
{
|
|
||||||
if (cur_marker->position < aif_data->loop_offset) {
|
|
||||||
aif_data->loop_offset = cur_marker->position;
|
|
||||||
aif_data->has_loop = true;
|
|
||||||
}
|
|
||||||
aif_data->num_samples = cur_marker->position;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(markers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a table of deltas between sample values in compressed PCM data.
|
|
||||||
const int gDeltaEncodingTable[] = {
|
|
||||||
0, 1, 4, 9, 16, 25, 36, 49,
|
|
||||||
-64, -49, -36, -25, -16, -9, -4, -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Bytes *delta_decompress(struct Bytes *delta, unsigned int expected_length)
|
|
||||||
{
|
|
||||||
struct Bytes *pcm = malloc(sizeof(struct Bytes));
|
|
||||||
pcm->length = expected_length;
|
|
||||||
pcm->data = malloc(pcm->length + 0x40);
|
|
||||||
|
|
||||||
uint8_t hi, lo;
|
|
||||||
unsigned int i = 0;
|
|
||||||
unsigned int j = 0;
|
|
||||||
int k;
|
|
||||||
int8_t base;
|
|
||||||
while (i < delta->length)
|
|
||||||
{
|
|
||||||
base = (int8_t)delta->data[i++];
|
|
||||||
pcm->data[j++] = (uint8_t)base;
|
|
||||||
if (i >= delta->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (j >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lo = delta->data[i] & 0xf;
|
|
||||||
base += gDeltaEncodingTable[lo];
|
|
||||||
pcm->data[j++] = base;
|
|
||||||
i++;
|
|
||||||
if (i >= delta->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (j >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (k = 0; k < 31; k++)
|
|
||||||
{
|
|
||||||
hi = (delta->data[i] >> 4) & 0xf;
|
|
||||||
base += gDeltaEncodingTable[hi];
|
|
||||||
pcm->data[j++] = base;
|
|
||||||
if (j >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lo = delta->data[i] & 0xf;
|
|
||||||
base += gDeltaEncodingTable[lo];
|
|
||||||
pcm->data[j++] = base;
|
|
||||||
i++;
|
|
||||||
if (i >= delta->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (j >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (j >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pcm->length = j;
|
|
||||||
return pcm;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_delta_index(uint8_t sample, uint8_t prev_sample)
|
|
||||||
{
|
|
||||||
int best_error = INT_MAX;
|
|
||||||
int best_index = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
uint8_t new_sample = prev_sample + gDeltaEncodingTable[i];
|
|
||||||
int error = sample > new_sample ? sample - new_sample : new_sample - sample;
|
|
||||||
|
|
||||||
if (error < best_error)
|
|
||||||
{
|
|
||||||
best_error = error;
|
|
||||||
best_index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return best_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Bytes *delta_compress(struct Bytes *pcm)
|
|
||||||
{
|
|
||||||
struct Bytes *delta = malloc(sizeof(struct Bytes));
|
|
||||||
// estimate the length so we can malloc
|
|
||||||
int num_blocks = pcm->length / 64;
|
|
||||||
delta->length = num_blocks * 33;
|
|
||||||
|
|
||||||
int extra = pcm->length % 64;
|
|
||||||
if (extra)
|
|
||||||
{
|
|
||||||
delta->length += 1;
|
|
||||||
extra -= 1;
|
|
||||||
}
|
|
||||||
if (extra)
|
|
||||||
{
|
|
||||||
delta->length += 1;
|
|
||||||
extra -= 1;
|
|
||||||
}
|
|
||||||
if (extra)
|
|
||||||
{
|
|
||||||
delta->length += (extra + 1) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
delta->data = malloc(delta->length + 33);
|
|
||||||
|
|
||||||
unsigned int i = 0;
|
|
||||||
unsigned int j = 0;
|
|
||||||
int k;
|
|
||||||
uint8_t base;
|
|
||||||
int delta_index;
|
|
||||||
|
|
||||||
while (i < pcm->length)
|
|
||||||
{
|
|
||||||
base = pcm->data[i++];
|
|
||||||
delta->data[j++] = base;
|
|
||||||
|
|
||||||
if (i >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
delta_index = get_delta_index(pcm->data[i++], base);
|
|
||||||
base += gDeltaEncodingTable[delta_index];
|
|
||||||
delta->data[j++] = delta_index;
|
|
||||||
|
|
||||||
for (k = 0; k < 31; k++)
|
|
||||||
{
|
|
||||||
if (i >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
delta_index = get_delta_index(pcm->data[i++], base);
|
|
||||||
base += gDeltaEncodingTable[delta_index];
|
|
||||||
delta->data[j] = (delta_index << 4);
|
|
||||||
|
|
||||||
if (i >= pcm->length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
delta_index = get_delta_index(pcm->data[i++], base);
|
|
||||||
base += gDeltaEncodingTable[delta_index];
|
|
||||||
delta->data[j++] |= delta_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delta->length = j;
|
|
||||||
|
|
||||||
return delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STORE_U32_LE(dest, value) \
|
|
||||||
do { \
|
|
||||||
*(dest) = (value) & 0xff; \
|
|
||||||
*((dest) + 1) = ((value) >> 8) & 0xff; \
|
|
||||||
*((dest) + 2) = ((value) >> 16) & 0xff; \
|
|
||||||
*((dest) + 3) = ((value) >> 24) & 0xff; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LOAD_U32_LE(var, src) \
|
|
||||||
do { \
|
|
||||||
(var) = *(src); \
|
|
||||||
(var) |= (*((src) + 1) << 8); \
|
|
||||||
(var) |= (*((src) + 2) << 16); \
|
|
||||||
(var) |= (*((src) + 3) << 24); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// Reads an .aif file and produces a .pcm file containing an array of 8-bit samples.
|
|
||||||
void aif2pcm(const char *aif_filename, const char *pcm_filename, bool compress)
|
|
||||||
{
|
|
||||||
struct Bytes *aif = read_bytearray(aif_filename);
|
|
||||||
AifData aif_data = {0,0,0,0,0,0,0};
|
|
||||||
read_aif(aif, &aif_data);
|
|
||||||
|
|
||||||
int header_size = 0x10;
|
|
||||||
struct Bytes *pcm;
|
|
||||||
struct Bytes output = {0,0};
|
|
||||||
|
|
||||||
if (compress)
|
|
||||||
{
|
|
||||||
struct Bytes *input = malloc(sizeof(struct Bytes));
|
|
||||||
input->data = aif_data.samples;
|
|
||||||
input->length = aif_data.real_num_samples;
|
|
||||||
pcm = delta_compress(input);
|
|
||||||
free(input);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pcm = malloc(sizeof(struct Bytes));
|
|
||||||
pcm->data = aif_data.samples;
|
|
||||||
pcm->length = aif_data.real_num_samples;
|
|
||||||
}
|
|
||||||
output.length = header_size + pcm->length;
|
|
||||||
output.data = malloc(output.length);
|
|
||||||
|
|
||||||
uint32_t pitch_adjust = (uint32_t)(aif_data.sample_rate * 1024);
|
|
||||||
uint32_t loop_offset = (uint32_t)(aif_data.loop_offset);
|
|
||||||
uint32_t adjusted_num_samples = (uint32_t)(aif_data.num_samples - 1);
|
|
||||||
uint32_t flags = 0;
|
|
||||||
if (aif_data.has_loop) flags |= 0x40000000;
|
|
||||||
if (compress) flags |= 1;
|
|
||||||
STORE_U32_LE(output.data + 0, flags);
|
|
||||||
STORE_U32_LE(output.data + 4, pitch_adjust);
|
|
||||||
STORE_U32_LE(output.data + 8, loop_offset);
|
|
||||||
STORE_U32_LE(output.data + 12, adjusted_num_samples);
|
|
||||||
memcpy(&output.data[header_size], pcm->data, pcm->length);
|
|
||||||
write_bytearray(pcm_filename, &output);
|
|
||||||
|
|
||||||
free(aif->data);
|
|
||||||
free(aif);
|
|
||||||
free(pcm);
|
|
||||||
free(output.data);
|
|
||||||
free(aif_data.samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads a .pcm file containing an array of 8-bit samples and produces an .aif file.
|
|
||||||
// See http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-1.3.pdf for .aif file specification.
|
|
||||||
void pcm2aif(const char *pcm_filename, const char *aif_filename, uint32_t base_note)
|
|
||||||
{
|
|
||||||
struct Bytes *pcm = read_bytearray(pcm_filename);
|
|
||||||
|
|
||||||
AifData *aif_data = malloc(sizeof(AifData));
|
|
||||||
|
|
||||||
uint32_t flags;
|
|
||||||
LOAD_U32_LE(flags, pcm->data + 0);
|
|
||||||
aif_data->has_loop = flags & 0x40000000;
|
|
||||||
bool compressed = flags & 1;
|
|
||||||
|
|
||||||
uint32_t pitch_adjust;
|
|
||||||
LOAD_U32_LE(pitch_adjust, pcm->data + 4);
|
|
||||||
aif_data->sample_rate = pitch_adjust / 1024.0;
|
|
||||||
|
|
||||||
LOAD_U32_LE(aif_data->loop_offset, pcm->data + 8);
|
|
||||||
LOAD_U32_LE(aif_data->num_samples, pcm->data + 12);
|
|
||||||
aif_data->num_samples += 1;
|
|
||||||
|
|
||||||
if (compressed)
|
|
||||||
{
|
|
||||||
struct Bytes *delta = pcm;
|
|
||||||
uint8_t *pcm_data = pcm->data;
|
|
||||||
delta->length -= 0x10;
|
|
||||||
delta->data += 0x10;
|
|
||||||
pcm = delta_decompress(delta, aif_data->num_samples);
|
|
||||||
free(pcm_data);
|
|
||||||
free(delta);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pcm->length -= 0x10;
|
|
||||||
pcm->data += 0x10;
|
|
||||||
}
|
|
||||||
|
|
||||||
aif_data->samples = malloc(pcm->length);
|
|
||||||
memcpy(aif_data->samples, pcm->data, pcm->length);
|
|
||||||
|
|
||||||
struct Bytes *aif = malloc(sizeof(struct Bytes));
|
|
||||||
aif->length = 54 + 60 + pcm->length;
|
|
||||||
aif->data = malloc(aif->length);
|
|
||||||
|
|
||||||
long pos = 0;
|
|
||||||
|
|
||||||
// First, write the FORM header chunk.
|
|
||||||
// FORM Chunk ckID
|
|
||||||
aif->data[pos++] = 'F';
|
|
||||||
aif->data[pos++] = 'O';
|
|
||||||
aif->data[pos++] = 'R';
|
|
||||||
aif->data[pos++] = 'M';
|
|
||||||
|
|
||||||
// FORM Chunk ckSize
|
|
||||||
unsigned long form_size = pos;
|
|
||||||
unsigned long data_size = aif->length - 8;
|
|
||||||
aif->data[pos++] = ((data_size >> 24) & 0xFF);
|
|
||||||
aif->data[pos++] = ((data_size >> 16) & 0xFF);
|
|
||||||
aif->data[pos++] = ((data_size >> 8) & 0xFF);
|
|
||||||
aif->data[pos++] = (data_size & 0xFF);
|
|
||||||
|
|
||||||
// FORM Chunk formType
|
|
||||||
aif->data[pos++] = 'A';
|
|
||||||
aif->data[pos++] = 'I';
|
|
||||||
aif->data[pos++] = 'F';
|
|
||||||
aif->data[pos++] = 'F';
|
|
||||||
|
|
||||||
// Next, write the Common Chunk
|
|
||||||
// Common Chunk ckID
|
|
||||||
aif->data[pos++] = 'C';
|
|
||||||
aif->data[pos++] = 'O';
|
|
||||||
aif->data[pos++] = 'M';
|
|
||||||
aif->data[pos++] = 'M';
|
|
||||||
|
|
||||||
// Common Chunk ckSize
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 18;
|
|
||||||
|
|
||||||
// Common Chunk numChannels
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 1; // 1 channel
|
|
||||||
|
|
||||||
// Common Chunk numSampleFrames
|
|
||||||
aif->data[pos++] = ((aif_data->num_samples >> 24) & 0xFF);
|
|
||||||
aif->data[pos++] = ((aif_data->num_samples >> 16) & 0xFF);
|
|
||||||
aif->data[pos++] = ((aif_data->num_samples >> 8) & 0xFF);
|
|
||||||
aif->data[pos++] = (aif_data->num_samples & 0xFF);
|
|
||||||
|
|
||||||
// Common Chunk sampleSize
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 8; // 8 bits per sample
|
|
||||||
|
|
||||||
// Common Chunk sampleRate
|
|
||||||
//double sample_rate = pitch_adjust / 1024.0;
|
|
||||||
uint8_t sample_rate_buffer[10];
|
|
||||||
ieee754_write_extended(aif_data->sample_rate, sample_rate_buffer);
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
aif->data[pos++] = sample_rate_buffer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aif_data->has_loop)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Marker Chunk ckID
|
|
||||||
aif->data[pos++] = 'M';
|
|
||||||
aif->data[pos++] = 'A';
|
|
||||||
aif->data[pos++] = 'R';
|
|
||||||
aif->data[pos++] = 'K';
|
|
||||||
|
|
||||||
// Marker Chunk ckSize
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 12 + (aif_data->has_loop ? 12 : 0);
|
|
||||||
|
|
||||||
// Marker Chunk numMarkers
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = (aif_data->has_loop ? 2 : 1);
|
|
||||||
|
|
||||||
// Marker loop start
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 1; // id = 1
|
|
||||||
|
|
||||||
long loop_start = aif_data->loop_offset;
|
|
||||||
aif->data[pos++] = ((loop_start >> 24) & 0xFF);
|
|
||||||
aif->data[pos++] = ((loop_start >> 16) & 0xFF);
|
|
||||||
aif->data[pos++] = ((loop_start >> 8) & 0xFF);
|
|
||||||
aif->data[pos++] = (loop_start & 0xFF); // position
|
|
||||||
|
|
||||||
aif->data[pos++] = 5; // pascal-style string length
|
|
||||||
aif->data[pos++] = 'S';
|
|
||||||
aif->data[pos++] = 'T';
|
|
||||||
aif->data[pos++] = 'A';
|
|
||||||
aif->data[pos++] = 'R';
|
|
||||||
aif->data[pos++] = 'T'; // markerName
|
|
||||||
|
|
||||||
// Marker loop end
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = (aif_data->has_loop ? 2 : 1); // id = 2
|
|
||||||
|
|
||||||
long loop_end = aif_data->num_samples;
|
|
||||||
aif->data[pos++] = ((loop_end >> 24) & 0xFF);
|
|
||||||
aif->data[pos++] = ((loop_end >> 16) & 0xFF);
|
|
||||||
aif->data[pos++] = ((loop_end >> 8) & 0xFF);
|
|
||||||
aif->data[pos++] = (loop_end & 0xFF); // position
|
|
||||||
|
|
||||||
aif->data[pos++] = 3; // pascal-style string length
|
|
||||||
aif->data[pos++] = 'E';
|
|
||||||
aif->data[pos++] = 'N';
|
|
||||||
aif->data[pos++] = 'D';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instrument Chunk ckID
|
|
||||||
aif->data[pos++] = 'I';
|
|
||||||
aif->data[pos++] = 'N';
|
|
||||||
aif->data[pos++] = 'S';
|
|
||||||
aif->data[pos++] = 'T';
|
|
||||||
|
|
||||||
// Instrument Chunk ckSize
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 20;
|
|
||||||
|
|
||||||
aif->data[pos++] = base_note; // baseNote
|
|
||||||
aif->data[pos++] = 0; // detune
|
|
||||||
aif->data[pos++] = 0; // lowNote
|
|
||||||
aif->data[pos++] = 127; // highNote
|
|
||||||
aif->data[pos++] = 1; // lowVelocity
|
|
||||||
aif->data[pos++] = 127; // highVelocity
|
|
||||||
aif->data[pos++] = 0; // gain (hi)
|
|
||||||
aif->data[pos++] = 0; // gain (lo)
|
|
||||||
|
|
||||||
// Instrument Chunk sustainLoop
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 1; // playMode = ForwardLooping
|
|
||||||
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 1; // beginLoop marker id
|
|
||||||
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 2; // endLoop marker id
|
|
||||||
|
|
||||||
// Instrument Chunk releaseLoop
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 1; // playMode = ForwardLooping
|
|
||||||
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 1; // beginLoop marker id
|
|
||||||
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 2; // endLoop marker id
|
|
||||||
|
|
||||||
// Finally, write the Sound Data Chunk
|
|
||||||
// Sound Data Chunk ckID
|
|
||||||
aif->data[pos++] = 'S';
|
|
||||||
aif->data[pos++] = 'S';
|
|
||||||
aif->data[pos++] = 'N';
|
|
||||||
aif->data[pos++] = 'D';
|
|
||||||
|
|
||||||
// Sound Data Chunk ckSize
|
|
||||||
unsigned long sound_data_size = pcm->length + 8;
|
|
||||||
aif->data[pos++] = ((sound_data_size >> 24) & 0xFF);
|
|
||||||
aif->data[pos++] = ((sound_data_size >> 16) & 0xFF);
|
|
||||||
aif->data[pos++] = ((sound_data_size >> 8) & 0xFF);
|
|
||||||
aif->data[pos++] = (sound_data_size & 0xFF);
|
|
||||||
|
|
||||||
// Sound Data Chunk offset
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
|
|
||||||
// Sound Data Chunk blockSize
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
aif->data[pos++] = 0;
|
|
||||||
|
|
||||||
// Sound Data Chunk soundData
|
|
||||||
for (unsigned int i = 0; i < aif_data->loop_offset; i++)
|
|
||||||
{
|
|
||||||
aif->data[pos++] = aif_data->samples[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
int j = 0;
|
|
||||||
for (unsigned int i = aif_data->loop_offset; i < pcm->length; i++)
|
|
||||||
{
|
|
||||||
int pcm_index = aif_data->loop_offset + (j++ % (pcm->length - aif_data->loop_offset));
|
|
||||||
aif->data[pos++] = aif_data->samples[pcm_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
aif->length = pos;
|
|
||||||
|
|
||||||
// Go back and rewrite ckSize
|
|
||||||
data_size = aif->length - 8;
|
|
||||||
aif->data[form_size + 0] = ((data_size >> 24) & 0xFF);
|
|
||||||
aif->data[form_size + 1] = ((data_size >> 16) & 0xFF);
|
|
||||||
aif->data[form_size + 2] = ((data_size >> 8) & 0xFF);
|
|
||||||
aif->data[form_size + 3] = (data_size & 0xFF);
|
|
||||||
|
|
||||||
write_bytearray(aif_filename, aif);
|
|
||||||
|
|
||||||
free(aif->data);
|
|
||||||
free(aif);
|
|
||||||
}
|
|
||||||
|
|
||||||
void usage(void)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: aif2pcm bin_file [aif_file]\n");
|
|
||||||
fprintf(stderr, " aif2pcm aif_file [bin_file] [--compress]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
if (argc < 2)
|
|
||||||
{
|
|
||||||
usage();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *input_file = argv[1];
|
|
||||||
char *extension = get_file_extension(input_file);
|
|
||||||
char *output_file;
|
|
||||||
bool compressed = false;
|
|
||||||
|
|
||||||
if (argc > 3)
|
|
||||||
{
|
|
||||||
for (int i = 3; i < argc; i++)
|
|
||||||
{
|
|
||||||
if (strcmp(argv[i], "--compress") == 0)
|
|
||||||
{
|
|
||||||
compressed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(extension, "aif") == 0 || strcmp(extension, "aiff") == 0)
|
|
||||||
{
|
|
||||||
if (argc >= 3)
|
|
||||||
{
|
|
||||||
output_file = argv[2];
|
|
||||||
aif2pcm(input_file, output_file, compressed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
output_file = new_file_extension(input_file, "bin");
|
|
||||||
aif2pcm(input_file, output_file, compressed);
|
|
||||||
free(output_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp(extension, "bin") == 0)
|
|
||||||
{
|
|
||||||
if (argc >= 3)
|
|
||||||
{
|
|
||||||
output_file = argv[2];
|
|
||||||
pcm2aif(input_file, output_file, 60);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
output_file = new_file_extension(input_file, "aif");
|
|
||||||
pcm2aif(input_file, output_file, 60);
|
|
||||||
free(output_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FATAL_ERROR("Input file must be .aif or .bin: '%s'\n", input_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
asset_processor
|
|
||||||
build/
|
|
|
@ -1,39 +0,0 @@
|
||||||
|
|
||||||
CXX = g++
|
|
||||||
CXXFLAGS = -O3 -Wall -Wextra -Werror -std=c++17
|
|
||||||
#CXXFLAGS += -g # debug
|
|
||||||
|
|
||||||
BUILD_FOLDER = build
|
|
||||||
|
|
||||||
SRCS = $(wildcard *.cpp)
|
|
||||||
SRCS += $(wildcard assets/*.cpp)
|
|
||||||
|
|
||||||
OBJS := $(patsubst %.cpp,$(BUILD_FOLDER)/%.o,$(SRCS))
|
|
||||||
|
|
||||||
INCLUDES = -I./
|
|
||||||
|
|
||||||
LIB :=
|
|
||||||
ifneq ($(shell uname -s),Darwin)
|
|
||||||
LIB += -lstdc++fs
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Create build dirs
|
|
||||||
$(shell mkdir -p $(dir $(OBJS)) >/dev/null)
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: asset_processor
|
|
||||||
|
|
||||||
asset_processor: $(OBJS)
|
|
||||||
$(CXX) -o asset_processor $(OBJS) $(LIB)
|
|
||||||
|
|
||||||
$(BUILD_FOLDER)/%.o: %.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) $(INCLUDES) -c -o $@ $<
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) asset_processor asset_processor.exe
|
|
||||||
$(RM) -r $(BUILD_FOLDER)
|
|
||||||
|
|
||||||
# Automatic dependencies
|
|
||||||
CXXFLAGS += -MMD
|
|
||||||
-include $(OBJS:.o=.d)
|
|
|
@ -1,36 +0,0 @@
|
||||||
#include "animation.h"
|
|
||||||
#include "reader.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
void AnimationAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
|
||||||
Reader reader(baserom, this->start, this->size);
|
|
||||||
std::vector<std::string> lines;
|
|
||||||
bool end_of_animation = false;
|
|
||||||
while (!end_of_animation && reader.cursor + 3 < this->size) {
|
|
||||||
u8 frame_index = reader.read_u8();
|
|
||||||
u8 keyframe_duration = reader.read_u8();
|
|
||||||
u8 bitfield = reader.read_u8();
|
|
||||||
u8 bitfield2 = reader.read_u8();
|
|
||||||
end_of_animation = (bitfield2 & 0x80) != 0;
|
|
||||||
lines.push_back(string_format("\tkeyframe frame_index=%d", frame_index));
|
|
||||||
lines.push_back(opt_param(", duration=%d", 0, keyframe_duration));
|
|
||||||
lines.push_back(opt_param(", bitfield=0x%x", 0, bitfield));
|
|
||||||
lines.push_back(opt_param(", bitfield2=0x%x", 0, bitfield2));
|
|
||||||
lines.push_back("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!end_of_animation) {
|
|
||||||
lines.push_back("@ TODO why no terminator?\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (reader.cursor < this->size) {
|
|
||||||
u8 keyframe_count = reader.read_u8();
|
|
||||||
lines.push_back(string_format("\t.byte %d @ keyframe count\n", keyframe_count));
|
|
||||||
}
|
|
||||||
std::ofstream out(this->assetPath);
|
|
||||||
for (const auto& line : lines) {
|
|
||||||
out << line;
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
#include "asset.h"
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
void BaseAsset::extractBinary(const std::vector<char>& baserom) {
|
|
||||||
auto first = baserom.begin() + this->start;
|
|
||||||
auto last = baserom.begin() + this->start + this->size;
|
|
||||||
std::vector<char> data(first, last);
|
|
||||||
std::fstream file(this->path, std::ios::out | std::ios::binary);
|
|
||||||
file.write(&data[0], data.size());
|
|
||||||
file.close();
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
#include "exitlist.h"
|
|
||||||
#include "reader.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
void ExitListAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
|
||||||
Reader reader(baserom, this->start, this->size);
|
|
||||||
std::vector<std::string> lines;
|
|
||||||
while (reader.cursor < this->size) {
|
|
||||||
u16 transition_type = reader.read_u16();
|
|
||||||
u16 x_pos = reader.read_u16();
|
|
||||||
u16 y_pos = reader.read_u16();
|
|
||||||
u16 dest_x = reader.read_u16();
|
|
||||||
u16 dest_y = reader.read_u16();
|
|
||||||
u8 screen_edge = reader.read_u8();
|
|
||||||
u8 dest_area = reader.read_u8();
|
|
||||||
u8 dest_room = reader.read_u8();
|
|
||||||
u8 unknown_2 = reader.read_u8();
|
|
||||||
u8 unknown_3 = reader.read_u8();
|
|
||||||
u8 unknown_4 = reader.read_u8();
|
|
||||||
u16 unknown_5 = reader.read_u16();
|
|
||||||
u16 padding_1 = reader.read_u16();
|
|
||||||
if (transition_type == 0xffff) {
|
|
||||||
lines.push_back("\texit_list_end\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lines.push_back(string_format("\texit transition=%d", transition_type));
|
|
||||||
lines.push_back(opt_param(", x=0x%x", 0, x_pos));
|
|
||||||
lines.push_back(opt_param(", y=0x%x", 0, y_pos));
|
|
||||||
lines.push_back(opt_param(", destX=0x%x", 0, dest_x));
|
|
||||||
lines.push_back(opt_param(", destY=0x%x", 0, dest_y));
|
|
||||||
lines.push_back(opt_param(", screenEdge=0x%x", 0, screen_edge));
|
|
||||||
lines.push_back(opt_param(", destArea=0x%x", 0, dest_area));
|
|
||||||
lines.push_back(opt_param(", destRoom=0x%x", 0, dest_room));
|
|
||||||
lines.push_back(opt_param(", unknown=0x%x", 0, unknown_2));
|
|
||||||
lines.push_back(opt_param(", unknown2=0x%x", 0, unknown_3));
|
|
||||||
lines.push_back(opt_param(", unknown3=0x%x", 0, unknown_4));
|
|
||||||
lines.push_back(opt_param(", unknown4=0x%x", 0, unknown_5));
|
|
||||||
lines.push_back(opt_param(", padding=0x%x", 0, padding_1));
|
|
||||||
lines.push_back("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ofstream out(this->assetPath);
|
|
||||||
for (const auto& line : lines) {
|
|
||||||
out << line;
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#include "macroasm.h"
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
std::filesystem::path BaseMacroAsmAsset::generateAssetPath() {
|
|
||||||
std::filesystem::path path = this->path;
|
|
||||||
return path.replace_extension(".s");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::path BaseMacroAsmAsset::generateBuildPath() {
|
|
||||||
std::filesystem::path path = this->path;
|
|
||||||
return path.replace_extension(".s");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseMacroAsmAsset::extractBinary(const std::vector<char>& baserom) {
|
|
||||||
BaseAsset::extractBinary(baserom);
|
|
||||||
// Create dummy .s file that just incbins the .bin file.
|
|
||||||
std::ofstream out(this->assetPath);
|
|
||||||
out << "\t.incbin " << this->path << "\n";
|
|
||||||
out.close();
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
#include "spriteframe.h"
|
|
||||||
#include "reader.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
void SpriteFrameAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
|
||||||
Reader reader(baserom, this->start, this->size);
|
|
||||||
std::vector<std::string> lines;
|
|
||||||
while (reader.cursor < this->size) {
|
|
||||||
u8 num_gfx_tiles = reader.read_u8();
|
|
||||||
u8 unk = reader.read_u8();
|
|
||||||
u16 first_gfx_tile_index = reader.read_u16();
|
|
||||||
|
|
||||||
lines.push_back(string_format("\tsprite_frame first_tile_index=0x%x", first_gfx_tile_index));
|
|
||||||
lines.push_back(opt_param(", num_tiles=%d", 0, num_gfx_tiles));
|
|
||||||
lines.push_back(opt_param(", unknown=0x%x", 0, unk));
|
|
||||||
lines.push_back("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ofstream out(this->assetPath);
|
|
||||||
for (const auto& line : lines) {
|
|
||||||
out << line;
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,78 +0,0 @@
|
||||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
|
||||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
|
||||||
|
|
||||||
#include <cstdint> // int64_t, uint64_t
|
|
||||||
#include <map> // map
|
|
||||||
#include <memory> // allocator
|
|
||||||
#include <string> // string
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief namespace for Niels Lohmann
|
|
||||||
@see https://github.com/nlohmann
|
|
||||||
@since version 1.0.0
|
|
||||||
*/
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
@brief default JSONSerializer template argument
|
|
||||||
|
|
||||||
This serializer ignores the template arguments and uses ADL
|
|
||||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
|
||||||
for serialization.
|
|
||||||
*/
|
|
||||||
template<typename T = void, typename SFINAE = void>
|
|
||||||
struct adl_serializer;
|
|
||||||
|
|
||||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
|
||||||
std::map,
|
|
||||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
|
||||||
class StringType = std::string, class BooleanType = bool,
|
|
||||||
class NumberIntegerType = std::int64_t,
|
|
||||||
class NumberUnsignedType = std::uint64_t,
|
|
||||||
class NumberFloatType = double,
|
|
||||||
template<typename U> class AllocatorType = std::allocator,
|
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
|
||||||
adl_serializer,
|
|
||||||
class BinaryType = std::vector<std::uint8_t>>
|
|
||||||
class basic_json;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief JSON Pointer
|
|
||||||
|
|
||||||
A JSON pointer defines a string syntax for identifying a specific value
|
|
||||||
within a JSON document. It can be used with functions `at` and
|
|
||||||
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
|
|
||||||
|
|
||||||
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
|
|
||||||
|
|
||||||
@since version 2.0.0
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_pointer;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief default JSON class
|
|
||||||
|
|
||||||
This type is the default specialization of the @ref basic_json class which
|
|
||||||
uses the standard template types.
|
|
||||||
|
|
||||||
@since version 1.0.0
|
|
||||||
*/
|
|
||||||
using json = basic_json<>;
|
|
||||||
|
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
|
||||||
struct ordered_map;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief ordered JSON class
|
|
||||||
|
|
||||||
This type preserves the insertion order of object keys.
|
|
||||||
|
|
||||||
@since version 3.9.0
|
|
||||||
*/
|
|
||||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
|
||||||
|
|
||||||
} // namespace nlohmann
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
|
|
@ -1,9 +0,0 @@
|
||||||
#include "offsets.h"
|
|
||||||
|
|
||||||
OffsetCalculator::OffsetCalculator(std::filesystem::path outputFile, int baseOffset): baseOffset(baseOffset) {
|
|
||||||
output = std::ofstream(outputFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OffsetCalculator::addAsset(int start, std::string symbol) {
|
|
||||||
this->output << "\t.equiv offset_" << symbol << ", " << start - this->baseOffset << std::endl;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#ifndef OFFSETS_H
|
|
||||||
#define OFFSETS_H
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class OffsetCalculator {
|
|
||||||
|
|
||||||
public:
|
|
||||||
OffsetCalculator(std::filesystem::path offsetsFile, int baseOffset);
|
|
||||||
void addAsset(int start, std::string symbol);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::ofstream output;
|
|
||||||
int baseOffset;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,10 +0,0 @@
|
||||||
#include "reader.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
std::string opt_param(const std::string& format, int defaultVal, int value) {
|
|
||||||
if (value != defaultVal) {
|
|
||||||
return string_format(format, value);
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
#ifndef READER_H
|
|
||||||
#define READER_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
typedef uint8_t u8;
|
|
||||||
typedef uint16_t u16;
|
|
||||||
typedef uint32_t u32;
|
|
||||||
typedef uint64_t u64;
|
|
||||||
typedef int8_t s8;
|
|
||||||
typedef int16_t s16;
|
|
||||||
typedef int32_t s32;
|
|
||||||
typedef int64_t s64;
|
|
||||||
|
|
||||||
class Reader {
|
|
||||||
public:
|
|
||||||
Reader(const std::vector<char>& baserom, int start, int size) {
|
|
||||||
auto first = baserom.begin() + start;
|
|
||||||
auto last = baserom.begin() + start + size;
|
|
||||||
data = std::vector<char>(first, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 read_u8() {
|
|
||||||
return this->data[this->cursor++];
|
|
||||||
}
|
|
||||||
|
|
||||||
s8 read_s8() {
|
|
||||||
return (s8)this->read_u8();
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 read_u16() {
|
|
||||||
u16 val = (u8)this->data[this->cursor] + (((u8)this->data[this->cursor + 1]) << 8);
|
|
||||||
this->cursor += 2;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 read_u32() {
|
|
||||||
u32 val = ((u8)this->data[this->cursor]) + (((u8)this->data[this->cursor + 1]) << 8) +
|
|
||||||
(((u8)this->data[this->cursor + 2]) << 16) + (((u8)this->data[this->cursor + 3]) << 24);
|
|
||||||
this->cursor += 4;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
int cursor = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<char> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,36 +0,0 @@
|
||||||
#ifndef UTIL_H
|
|
||||||
#define UTIL_H
|
|
||||||
|
|
||||||
#include <json_fwd.hpp>
|
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
typedef uint8_t u8;
|
|
||||||
typedef uint16_t u16;
|
|
||||||
typedef uint32_t u32;
|
|
||||||
typedef uint64_t u64;
|
|
||||||
typedef int8_t s8;
|
|
||||||
typedef int16_t s16;
|
|
||||||
typedef int32_t s32;
|
|
||||||
typedef int64_t s64;
|
|
||||||
|
|
||||||
void check_call(const std::vector<std::string>& cmd);
|
|
||||||
|
|
||||||
template <typename... Args> std::string string_format(const std::string& format, Args... args) {
|
|
||||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
|
||||||
if (size_s <= 0) {
|
|
||||||
throw std::runtime_error("Error during formatting.");
|
|
||||||
}
|
|
||||||
auto size = static_cast<size_t>(size_s);
|
|
||||||
auto buf = std::make_unique<char[]>(size);
|
|
||||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
|
||||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string opt_param(const std::string& format, int defaultVal, int value);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1 +0,0 @@
|
||||||
bin2c
|
|
|
@ -1,16 +0,0 @@
|
||||||
CC = gcc
|
|
||||||
|
|
||||||
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
SRCS = bin2c.c
|
|
||||||
|
|
||||||
all: bin2c
|
|
||||||
@:
|
|
||||||
|
|
||||||
bin2c: $(SRCS)
|
|
||||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) bin2c bin2c.exe
|
|
|
@ -1,2 +0,0 @@
|
||||||
gbafix
|
|
||||||
README
|
|
3147
tools/gbafix/elf.h
3147
tools/gbafix/elf.h
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
||||||
gbagfx
|
|
|
@ -1,21 +0,0 @@
|
||||||
CC = gcc
|
|
||||||
|
|
||||||
CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK
|
|
||||||
|
|
||||||
LIBS = -lpng -lz
|
|
||||||
|
|
||||||
SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c huff.c
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: gbagfx
|
|
||||||
@:
|
|
||||||
|
|
||||||
gbagfx-debug: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h
|
|
||||||
$(CC) $(CFLAGS) -DDEBUG $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
gbagfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h
|
|
||||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) gbagfx gbagfx.exe
|
|
|
@ -1,12 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef CONVERT_PNG_H
|
|
||||||
#define CONVERT_PNG_H
|
|
||||||
|
|
||||||
#include "gfx.h"
|
|
||||||
|
|
||||||
void ReadPng(char *path, struct Image *image);
|
|
||||||
void WritePng(char *path, struct Image *image);
|
|
||||||
void ReadPngPalette(char *path, struct Palette *palette);
|
|
||||||
|
|
||||||
#endif // CONVERT_PNG_H
|
|
|
@ -1,326 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "global.h"
|
|
||||||
#include "font.h"
|
|
||||||
#include "gfx.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
unsigned char gFontPalette[][3] = {
|
|
||||||
{0x90, 0xC8, 0xFF}, // bg (saturated blue that contrasts well with the shadow color)
|
|
||||||
{0x38, 0x38, 0x38}, // fg (dark grey)
|
|
||||||
{0xD8, 0xD8, 0xD8}, // shadow (light grey)
|
|
||||||
{0xFF, 0xFF, 0xFF} // box (white)
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
|
||||||
{
|
|
||||||
unsigned int srcPixelsOffset = 0;
|
|
||||||
|
|
||||||
for (unsigned int row = 0; row < numRows; row++) {
|
|
||||||
for (unsigned int column = 0; column < 16; column++) {
|
|
||||||
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
|
|
||||||
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 8; i++) {
|
|
||||||
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
|
|
||||||
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
|
|
||||||
|
|
||||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
|
||||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
|
||||||
|
|
||||||
srcPixelsOffset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
|
||||||
{
|
|
||||||
unsigned int destPixelsOffset = 0;
|
|
||||||
|
|
||||||
for (unsigned int row = 0; row < numRows; row++) {
|
|
||||||
for (unsigned int column = 0; column < 16; column++) {
|
|
||||||
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
|
|
||||||
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 8; i++) {
|
|
||||||
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
|
|
||||||
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
|
|
||||||
|
|
||||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
|
||||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
|
||||||
|
|
||||||
destPixelsOffset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
|
||||||
{
|
|
||||||
for (unsigned int row = 0; row < numRows; row++) {
|
|
||||||
for (unsigned int column = 0; column < 16; column++) {
|
|
||||||
unsigned int glyphIndex = (row * 16) + column;
|
|
||||||
|
|
||||||
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
|
|
||||||
unsigned int pixelsX = column * 8;
|
|
||||||
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 8; i++) {
|
|
||||||
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
|
|
||||||
unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
|
|
||||||
|
|
||||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
|
||||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
|
||||||
|
|
||||||
srcPixelsOffset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertToHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
|
||||||
{
|
|
||||||
for (unsigned int row = 0; row < numRows; row++) {
|
|
||||||
for (unsigned int column = 0; column < 16; column++) {
|
|
||||||
unsigned int glyphIndex = (row * 16) + column;
|
|
||||||
|
|
||||||
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
|
|
||||||
unsigned int pixelsX = column * 8;
|
|
||||||
unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 8; i++) {
|
|
||||||
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
|
|
||||||
unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
|
|
||||||
|
|
||||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
|
||||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
|
||||||
|
|
||||||
destPixelsOffset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertFromFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
|
||||||
{
|
|
||||||
for (unsigned int row = 0; row < numRows; row++) {
|
|
||||||
for (unsigned int column = 0; column < 16; column++) {
|
|
||||||
unsigned int glyphIndex = (row * 16) + column;
|
|
||||||
|
|
||||||
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
|
|
||||||
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
|
|
||||||
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 8; i++) {
|
|
||||||
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
|
|
||||||
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
|
|
||||||
|
|
||||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
|
||||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
|
||||||
|
|
||||||
srcPixelsOffset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
|
||||||
{
|
|
||||||
for (unsigned int row = 0; row < numRows; row++) {
|
|
||||||
for (unsigned int column = 0; column < 16; column++) {
|
|
||||||
unsigned int glyphIndex = (row * 16) + column;
|
|
||||||
|
|
||||||
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
|
|
||||||
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
|
|
||||||
unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < 8; i++) {
|
|
||||||
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
|
|
||||||
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
|
|
||||||
|
|
||||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
|
||||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
|
||||||
|
|
||||||
destPixelsOffset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetFontPalette(struct Image *image)
|
|
||||||
{
|
|
||||||
image->hasPalette = true;
|
|
||||||
|
|
||||||
image->palette.numColors = 4;
|
|
||||||
|
|
||||||
for (int i = 0; i < image->palette.numColors; i++) {
|
|
||||||
image->palette.colors[i].red = gFontPalette[i][0];
|
|
||||||
image->palette.colors[i].green = gFontPalette[i][1];
|
|
||||||
image->palette.colors[i].blue = gFontPalette[i][2];
|
|
||||||
}
|
|
||||||
|
|
||||||
image->hasTransparency = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadLatinFont(char *path, struct Image *image)
|
|
||||||
{
|
|
||||||
int fileSize;
|
|
||||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
|
||||||
|
|
||||||
int numGlyphs = fileSize / 64;
|
|
||||||
|
|
||||||
if (numGlyphs % 16 != 0)
|
|
||||||
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
|
|
||||||
|
|
||||||
int numRows = numGlyphs / 16;
|
|
||||||
|
|
||||||
image->width = 256;
|
|
||||||
image->height = numRows * 16;
|
|
||||||
image->bitDepth = 2;
|
|
||||||
image->pixels = malloc(fileSize);
|
|
||||||
|
|
||||||
if (image->pixels == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
|
||||||
|
|
||||||
ConvertFromLatinFont(buffer, image->pixels, numRows);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
SetFontPalette(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteLatinFont(char *path, struct Image *image)
|
|
||||||
{
|
|
||||||
if (image->width != 256)
|
|
||||||
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
|
|
||||||
|
|
||||||
if (image->height % 16 != 0)
|
|
||||||
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
|
|
||||||
|
|
||||||
int numRows = image->height / 16;
|
|
||||||
int bufferSize = numRows * 16 * 64;
|
|
||||||
unsigned char *buffer = malloc(bufferSize);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
|
||||||
|
|
||||||
ConvertToLatinFont(image->pixels, buffer, numRows);
|
|
||||||
|
|
||||||
WriteWholeFile(path, buffer, bufferSize);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadHalfwidthJapaneseFont(char *path, struct Image *image)
|
|
||||||
{
|
|
||||||
int fileSize;
|
|
||||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
|
||||||
|
|
||||||
int glyphSize = 32;
|
|
||||||
|
|
||||||
if (fileSize % glyphSize != 0)
|
|
||||||
FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize);
|
|
||||||
|
|
||||||
int numGlyphs = fileSize / glyphSize;
|
|
||||||
|
|
||||||
if (numGlyphs % 16 != 0)
|
|
||||||
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
|
|
||||||
|
|
||||||
int numRows = numGlyphs / 16;
|
|
||||||
|
|
||||||
image->width = 128;
|
|
||||||
image->height = numRows * 16;
|
|
||||||
image->bitDepth = 2;
|
|
||||||
image->pixels = malloc(fileSize);
|
|
||||||
|
|
||||||
if (image->pixels == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
|
||||||
|
|
||||||
ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
SetFontPalette(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteHalfwidthJapaneseFont(char *path, struct Image *image)
|
|
||||||
{
|
|
||||||
if (image->width != 128)
|
|
||||||
FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width);
|
|
||||||
|
|
||||||
if (image->height % 16 != 0)
|
|
||||||
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
|
|
||||||
|
|
||||||
int numRows = image->height / 16;
|
|
||||||
int bufferSize = numRows * 16 * 32;
|
|
||||||
unsigned char *buffer = malloc(bufferSize);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
|
||||||
|
|
||||||
ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows);
|
|
||||||
|
|
||||||
WriteWholeFile(path, buffer, bufferSize);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadFullwidthJapaneseFont(char *path, struct Image *image)
|
|
||||||
{
|
|
||||||
int fileSize;
|
|
||||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
|
||||||
|
|
||||||
int numGlyphs = fileSize / 64;
|
|
||||||
|
|
||||||
if (numGlyphs % 16 != 0)
|
|
||||||
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
|
|
||||||
|
|
||||||
int numRows = numGlyphs / 16;
|
|
||||||
|
|
||||||
image->width = 256;
|
|
||||||
image->height = numRows * 16;
|
|
||||||
image->bitDepth = 2;
|
|
||||||
image->pixels = malloc(fileSize);
|
|
||||||
|
|
||||||
if (image->pixels == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
|
||||||
|
|
||||||
ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
SetFontPalette(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteFullwidthJapaneseFont(char *path, struct Image *image)
|
|
||||||
{
|
|
||||||
if (image->width != 256)
|
|
||||||
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
|
|
||||||
|
|
||||||
if (image->height % 16 != 0)
|
|
||||||
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
|
|
||||||
|
|
||||||
int numRows = image->height / 16;
|
|
||||||
int bufferSize = numRows * 16 * 64;
|
|
||||||
unsigned char *buffer = malloc(bufferSize);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
|
||||||
|
|
||||||
ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows);
|
|
||||||
|
|
||||||
WriteWholeFile(path, buffer, bufferSize);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef FONT_H
|
|
||||||
#define FONT_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "gfx.h"
|
|
||||||
|
|
||||||
void ReadLatinFont(char *path, struct Image *image);
|
|
||||||
void WriteLatinFont(char *path, struct Image *image);
|
|
||||||
void ReadHalfwidthJapaneseFont(char *path, struct Image *image);
|
|
||||||
void WriteHalfwidthJapaneseFont(char *path, struct Image *image);
|
|
||||||
void ReadFullwidthJapaneseFont(char *path, struct Image *image);
|
|
||||||
void WriteFullwidthJapaneseFont(char *path, struct Image *image);
|
|
||||||
|
|
||||||
#endif // FONT_H
|
|
|
@ -1,344 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "global.h"
|
|
||||||
#include "gfx.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#define GET_GBA_PAL_RED(x) (((x) >> 0) & 0x1F)
|
|
||||||
#define GET_GBA_PAL_GREEN(x) (((x) >> 5) & 0x1F)
|
|
||||||
#define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F)
|
|
||||||
|
|
||||||
#define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r))
|
|
||||||
|
|
||||||
#define UPCONVERT_BIT_DEPTH(x) (((x) * 255) / 31)
|
|
||||||
|
|
||||||
#define DOWNCONVERT_BIT_DEPTH(x) ((x) / 8)
|
|
||||||
|
|
||||||
static void AdvanceMetatilePosition(int *subTileX, int *subTileY, int *metatileX, int *metatileY, int metatilesWide, int metatileWidth, int metatileHeight)
|
|
||||||
{
|
|
||||||
(*subTileX)++;
|
|
||||||
if (*subTileX == metatileWidth) {
|
|
||||||
*subTileX = 0;
|
|
||||||
(*subTileY)++;
|
|
||||||
if (*subTileY == metatileHeight) {
|
|
||||||
*subTileY = 0;
|
|
||||||
(*metatileX)++;
|
|
||||||
if (*metatileX == metatilesWide) {
|
|
||||||
*metatileX = 0;
|
|
||||||
(*metatileY)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
|
|
||||||
{
|
|
||||||
int subTileX = 0;
|
|
||||||
int subTileY = 0;
|
|
||||||
int metatileX = 0;
|
|
||||||
int metatileY = 0;
|
|
||||||
int pitch = metatilesWide * metatileWidth;
|
|
||||||
|
|
||||||
for (int i = 0; i < numTiles; i++) {
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
|
|
||||||
int destX = metatileX * metatileWidth + subTileX;
|
|
||||||
unsigned char srcPixelOctet = *src++;
|
|
||||||
unsigned char *destPixelOctet = &dest[destY * pitch + destX];
|
|
||||||
|
|
||||||
for (int k = 0; k < 8; k++) {
|
|
||||||
*destPixelOctet <<= 1;
|
|
||||||
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
|
|
||||||
srcPixelOctet >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
|
|
||||||
{
|
|
||||||
int subTileX = 0;
|
|
||||||
int subTileY = 0;
|
|
||||||
int metatileX = 0;
|
|
||||||
int metatileY = 0;
|
|
||||||
int pitch = (metatilesWide * metatileWidth) * 4;
|
|
||||||
|
|
||||||
for (int i = 0; i < numTiles; i++) {
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
|
|
||||||
|
|
||||||
for (int k = 0; k < 4; k++) {
|
|
||||||
int destX = (metatileX * metatileWidth + subTileX) * 4 + k;
|
|
||||||
unsigned char srcPixelPair = *src++;
|
|
||||||
unsigned char leftPixel = srcPixelPair & 0xF;
|
|
||||||
unsigned char rightPixel = srcPixelPair >> 4;
|
|
||||||
|
|
||||||
if (invertColors) {
|
|
||||||
leftPixel = 15 - leftPixel;
|
|
||||||
rightPixel = 15 - rightPixel;
|
|
||||||
}
|
|
||||||
|
|
||||||
dest[destY * pitch + destX] = (leftPixel << 4) | rightPixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
|
|
||||||
{
|
|
||||||
int subTileX = 0;
|
|
||||||
int subTileY = 0;
|
|
||||||
int metatileX = 0;
|
|
||||||
int metatileY = 0;
|
|
||||||
int pitch = (metatilesWide * metatileWidth) * 8;
|
|
||||||
|
|
||||||
for (int i = 0; i < numTiles; i++) {
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
|
|
||||||
|
|
||||||
for (int k = 0; k < 8; k++) {
|
|
||||||
int destX = (metatileX * metatileWidth + subTileX) * 8 + k;
|
|
||||||
unsigned char srcPixel = *src++;
|
|
||||||
|
|
||||||
if (invertColors)
|
|
||||||
srcPixel = 255 - srcPixel;
|
|
||||||
|
|
||||||
dest[destY * pitch + destX] = srcPixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
|
|
||||||
{
|
|
||||||
int subTileX = 0;
|
|
||||||
int subTileY = 0;
|
|
||||||
int metatileX = 0;
|
|
||||||
int metatileY = 0;
|
|
||||||
int pitch = metatilesWide * metatileWidth;
|
|
||||||
|
|
||||||
for (int i = 0; i < numTiles; i++) {
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
|
|
||||||
int srcX = metatileX * metatileWidth + subTileX;
|
|
||||||
unsigned char srcPixelOctet = src[srcY * pitch + srcX];
|
|
||||||
unsigned char *destPixelOctet = dest++;
|
|
||||||
|
|
||||||
for (int k = 0; k < 8; k++) {
|
|
||||||
*destPixelOctet <<= 1;
|
|
||||||
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
|
|
||||||
srcPixelOctet >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
|
|
||||||
{
|
|
||||||
int subTileX = 0;
|
|
||||||
int subTileY = 0;
|
|
||||||
int metatileX = 0;
|
|
||||||
int metatileY = 0;
|
|
||||||
int pitch = (metatilesWide * metatileWidth) * 4;
|
|
||||||
|
|
||||||
for (int i = 0; i < numTiles; i++) {
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
|
|
||||||
|
|
||||||
for (int k = 0; k < 4; k++) {
|
|
||||||
int srcX = (metatileX * metatileWidth + subTileX) * 4 + k;
|
|
||||||
unsigned char srcPixelPair = src[srcY * pitch + srcX];
|
|
||||||
unsigned char leftPixel = srcPixelPair >> 4;
|
|
||||||
unsigned char rightPixel = srcPixelPair & 0xF;
|
|
||||||
|
|
||||||
if (invertColors) {
|
|
||||||
leftPixel = 15 - leftPixel;
|
|
||||||
rightPixel = 15 - rightPixel;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest++ = (rightPixel << 4) | leftPixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
|
|
||||||
{
|
|
||||||
int subTileX = 0;
|
|
||||||
int subTileY = 0;
|
|
||||||
int metatileX = 0;
|
|
||||||
int metatileY = 0;
|
|
||||||
int pitch = (metatilesWide * metatileWidth) * 8;
|
|
||||||
|
|
||||||
for (int i = 0; i < numTiles; i++) {
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
|
|
||||||
|
|
||||||
for (int k = 0; k < 8; k++) {
|
|
||||||
int srcX = (metatileX * metatileWidth + subTileX) * 8 + k;
|
|
||||||
unsigned char srcPixel = src[srcY * pitch + srcX];
|
|
||||||
|
|
||||||
if (invertColors)
|
|
||||||
srcPixel = 255 - srcPixel;
|
|
||||||
|
|
||||||
*dest++ = srcPixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
|
||||||
{
|
|
||||||
int tileSize = bitDepth * 8;
|
|
||||||
|
|
||||||
int fileSize;
|
|
||||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
|
||||||
|
|
||||||
int numTiles = fileSize / tileSize;
|
|
||||||
|
|
||||||
int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth;
|
|
||||||
|
|
||||||
if (tilesWidth % metatileWidth != 0)
|
|
||||||
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth);
|
|
||||||
|
|
||||||
if (tilesHeight % metatileHeight != 0)
|
|
||||||
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
|
|
||||||
|
|
||||||
image->width = tilesWidth * 8;
|
|
||||||
image->height = tilesHeight * 8;
|
|
||||||
image->bitDepth = bitDepth;
|
|
||||||
image->pixels = calloc(tilesWidth * tilesHeight, tileSize);
|
|
||||||
|
|
||||||
if (image->pixels == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for pixels.\n");
|
|
||||||
|
|
||||||
int metatilesWide = tilesWidth / metatileWidth;
|
|
||||||
|
|
||||||
switch (bitDepth) {
|
|
||||||
case 1:
|
|
||||||
ConvertFromTiles1Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
ConvertFromTiles4Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
ConvertFromTiles8Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
|
||||||
{
|
|
||||||
int tileSize = bitDepth * 8;
|
|
||||||
|
|
||||||
if (image->width % 8 != 0)
|
|
||||||
FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width);
|
|
||||||
|
|
||||||
if (image->height % 8 != 0)
|
|
||||||
FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height);
|
|
||||||
|
|
||||||
int tilesWidth = image->width / 8;
|
|
||||||
int tilesHeight = image->height / 8;
|
|
||||||
|
|
||||||
if (tilesWidth % metatileWidth != 0)
|
|
||||||
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth);
|
|
||||||
|
|
||||||
if (tilesHeight % metatileHeight != 0)
|
|
||||||
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
|
|
||||||
|
|
||||||
int maxNumTiles = tilesWidth * tilesHeight;
|
|
||||||
|
|
||||||
if (numTiles == 0)
|
|
||||||
numTiles = maxNumTiles;
|
|
||||||
else if (numTiles > maxNumTiles)
|
|
||||||
FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles);
|
|
||||||
|
|
||||||
int bufferSize = numTiles * tileSize;
|
|
||||||
unsigned char *buffer = malloc(bufferSize);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for pixels.\n");
|
|
||||||
|
|
||||||
int metatilesWide = tilesWidth / metatileWidth;
|
|
||||||
|
|
||||||
switch (bitDepth) {
|
|
||||||
case 1:
|
|
||||||
ConvertToTiles1Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
ConvertToTiles4Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
ConvertToTiles8Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteWholeFile(path, buffer, bufferSize);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeImage(struct Image *image)
|
|
||||||
{
|
|
||||||
free(image->pixels);
|
|
||||||
image->pixels = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadGbaPalette(char *path, struct Palette *palette)
|
|
||||||
{
|
|
||||||
int fileSize;
|
|
||||||
unsigned char *data = ReadWholeFile(path, &fileSize);
|
|
||||||
|
|
||||||
if (fileSize % 2 != 0)
|
|
||||||
FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize);
|
|
||||||
|
|
||||||
palette->numColors = fileSize / 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < palette->numColors; i++) {
|
|
||||||
uint16_t paletteEntry = (data[i * 2 + 1] << 8) | data[i * 2];
|
|
||||||
palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry));
|
|
||||||
palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry));
|
|
||||||
palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteGbaPalette(char *path, struct Palette *palette)
|
|
||||||
{
|
|
||||||
FILE *fp = fopen(path, "wb");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
|
|
||||||
|
|
||||||
for (int i = 0; i < palette->numColors; i++) {
|
|
||||||
unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red);
|
|
||||||
unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green);
|
|
||||||
unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue);
|
|
||||||
|
|
||||||
uint16_t paletteEntry = SET_GBA_PAL(red, green, blue);
|
|
||||||
|
|
||||||
fputc(paletteEntry & 0xFF, fp);
|
|
||||||
fputc(paletteEntry >> 8, fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef GFX_H
|
|
||||||
#define GFX_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
struct Color {
|
|
||||||
unsigned char red;
|
|
||||||
unsigned char green;
|
|
||||||
unsigned char blue;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Palette {
|
|
||||||
struct Color colors[256];
|
|
||||||
int numColors;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Image {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int bitDepth;
|
|
||||||
unsigned char *pixels;
|
|
||||||
bool hasPalette;
|
|
||||||
struct Palette palette;
|
|
||||||
bool hasTransparency;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
|
||||||
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
|
||||||
void FreeImage(struct Image *image);
|
|
||||||
void ReadGbaPalette(char *path, struct Palette *palette);
|
|
||||||
void WriteGbaPalette(char *path, struct Palette *palette);
|
|
||||||
|
|
||||||
#endif // GFX_H
|
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef GLOBAL_H
|
|
||||||
#define GLOBAL_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
|
|
||||||
#define FATAL_ERROR(format, ...) \
|
|
||||||
do { \
|
|
||||||
fprintf(stderr, format, __VA_ARGS__); \
|
|
||||||
exit(1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define UNUSED
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define FATAL_ERROR(format, ...) \
|
|
||||||
do { \
|
|
||||||
fprintf(stderr, format, ##__VA_ARGS__); \
|
|
||||||
exit(1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define UNUSED __attribute__((__unused__))
|
|
||||||
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
#endif // GLOBAL_H
|
|
|
@ -1,38 +0,0 @@
|
||||||
#ifndef HUFF_H
|
|
||||||
#define HUFF_H
|
|
||||||
|
|
||||||
union HuffNode;
|
|
||||||
|
|
||||||
struct HuffData {
|
|
||||||
unsigned value:31;
|
|
||||||
unsigned isLeaf:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HuffLeaf {
|
|
||||||
struct HuffData header;
|
|
||||||
unsigned char key;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HuffBranch {
|
|
||||||
struct HuffData header;
|
|
||||||
union HuffNode * left;
|
|
||||||
union HuffNode * right;
|
|
||||||
};
|
|
||||||
|
|
||||||
union HuffNode {
|
|
||||||
struct HuffData header;
|
|
||||||
struct HuffLeaf leaf;
|
|
||||||
struct HuffBranch branch;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef union HuffNode HuffNode_t;
|
|
||||||
|
|
||||||
struct BitEncoding {
|
|
||||||
unsigned long long nbits:6;
|
|
||||||
unsigned long long bitstring:58;
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned char * HuffCompress(unsigned char * buffer, int srcSize, int * compressedSize_p, int bitDepth);
|
|
||||||
unsigned char * HuffDecompress(unsigned char * buffer, int srcSize, int * uncompressedSize_p);
|
|
||||||
|
|
||||||
#endif //HUFF_H
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef JASC_PAL_H
|
|
||||||
#define JASC_PAL_H
|
|
||||||
|
|
||||||
void ReadJascPalette(char *path, struct Palette *palette);
|
|
||||||
void WriteJascPalette(char *path, struct Palette *palette);
|
|
||||||
|
|
||||||
#endif // JASC_PAL_H
|
|
|
@ -1,153 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "global.h"
|
|
||||||
#include "lz.h"
|
|
||||||
|
|
||||||
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
|
|
||||||
{
|
|
||||||
if (srcSize < 4)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
|
|
||||||
|
|
||||||
unsigned char *dest = malloc(destSize);
|
|
||||||
|
|
||||||
if (dest == NULL)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
int srcPos = 4;
|
|
||||||
int destPos = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (srcPos >= srcSize)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
unsigned char flags = src[srcPos++];
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
if (flags & 0x80) {
|
|
||||||
if (srcPos + 1 >= srcSize)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
int blockSize = (src[srcPos] >> 4) + 3;
|
|
||||||
int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1;
|
|
||||||
|
|
||||||
srcPos += 2;
|
|
||||||
|
|
||||||
int blockPos = destPos - blockDistance;
|
|
||||||
|
|
||||||
// Some Ruby/Sapphire tilesets overflow.
|
|
||||||
if (destPos + blockSize > destSize) {
|
|
||||||
blockSize = destSize - destPos;
|
|
||||||
fprintf(stderr, "Destination buffer overflow.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blockPos < 0)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
for (int j = 0; j < blockSize; j++)
|
|
||||||
dest[destPos++] = dest[blockPos + j];
|
|
||||||
} else {
|
|
||||||
if (srcPos >= srcSize || destPos >= destSize)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
dest[destPos++] = src[srcPos++];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destPos == destSize) {
|
|
||||||
*uncompressedSize = destSize;
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
|
||||||
FATAL_ERROR("Fatal error while decompressing LZ file.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance)
|
|
||||||
{
|
|
||||||
if (srcSize <= 0)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8);
|
|
||||||
|
|
||||||
// Round up to the next multiple of four.
|
|
||||||
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
|
|
||||||
|
|
||||||
unsigned char *dest = malloc(worstCaseDestSize);
|
|
||||||
|
|
||||||
if (dest == NULL)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
// header
|
|
||||||
dest[0] = 0x10; // LZ compression type
|
|
||||||
dest[1] = (unsigned char)srcSize;
|
|
||||||
dest[2] = (unsigned char)(srcSize >> 8);
|
|
||||||
dest[3] = (unsigned char)(srcSize >> 16);
|
|
||||||
|
|
||||||
int srcPos = 0;
|
|
||||||
int destPos = 4;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
unsigned char *flags = &dest[destPos++];
|
|
||||||
*flags = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
int bestBlockDistance = 0;
|
|
||||||
int bestBlockSize = 0;
|
|
||||||
int blockDistance = minDistance;
|
|
||||||
|
|
||||||
while (blockDistance <= srcPos && blockDistance <= 0x1000) {
|
|
||||||
int blockStart = srcPos - blockDistance;
|
|
||||||
int blockSize = 0;
|
|
||||||
|
|
||||||
while (blockSize < 18
|
|
||||||
&& srcPos + blockSize < srcSize
|
|
||||||
&& src[blockStart + blockSize] == src[srcPos + blockSize])
|
|
||||||
blockSize++;
|
|
||||||
|
|
||||||
if (blockSize > bestBlockSize) {
|
|
||||||
bestBlockDistance = blockDistance;
|
|
||||||
bestBlockSize = blockSize;
|
|
||||||
|
|
||||||
if (blockSize == 18)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockDistance++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestBlockSize >= 3) {
|
|
||||||
*flags |= (0x80 >> i);
|
|
||||||
srcPos += bestBlockSize;
|
|
||||||
bestBlockSize -= 3;
|
|
||||||
bestBlockDistance--;
|
|
||||||
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
|
|
||||||
dest[destPos++] = (unsigned char)bestBlockDistance;
|
|
||||||
} else {
|
|
||||||
dest[destPos++] = src[srcPos++];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPos == srcSize) {
|
|
||||||
// Pad to multiple of 4 bytes.
|
|
||||||
int remainder = destPos % 4;
|
|
||||||
|
|
||||||
if (remainder != 0) {
|
|
||||||
for (int i = 0; i < 4 - remainder; i++)
|
|
||||||
dest[destPos++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*compressedSize = destPos;
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
|
||||||
FATAL_ERROR("Fatal error while compressing LZ file.\n");
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef LZ_H
|
|
||||||
#define LZ_H
|
|
||||||
|
|
||||||
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
|
|
||||||
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance);
|
|
||||||
|
|
||||||
#endif // LZ_H
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright (c) 2016 YamaArashi
|
|
||||||
|
|
||||||
#ifndef RL_H
|
|
||||||
#define RL_H
|
|
||||||
|
|
||||||
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
|
|
||||||
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize);
|
|
||||||
|
|
||||||
#endif // RL_H
|
|
|
@ -1,124 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include "global.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
bool ParseNumber(char *s, char **end, int radix, int *intValue)
|
|
||||||
{
|
|
||||||
char *localEnd;
|
|
||||||
|
|
||||||
if (end == NULL)
|
|
||||||
end = &localEnd;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
const long longValue = strtol(s, end, radix);
|
|
||||||
|
|
||||||
if (*end == s)
|
|
||||||
return false; // not a number
|
|
||||||
|
|
||||||
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (longValue > INT_MAX)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (longValue < INT_MIN)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*intValue = (int)longValue;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *GetFileExtension(char *path)
|
|
||||||
{
|
|
||||||
char *extension = path;
|
|
||||||
|
|
||||||
while (*extension != 0)
|
|
||||||
extension++;
|
|
||||||
|
|
||||||
while (extension > path && *extension != '.')
|
|
||||||
extension--;
|
|
||||||
|
|
||||||
if (extension == path)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
extension++;
|
|
||||||
|
|
||||||
if (*extension == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *ReadWholeFile(char *path, int *size)
|
|
||||||
{
|
|
||||||
FILE *fp = fopen(path, "rb");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
|
|
||||||
*size = ftell(fp);
|
|
||||||
|
|
||||||
unsigned char *buffer = malloc(*size);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
|
|
||||||
|
|
||||||
rewind(fp);
|
|
||||||
|
|
||||||
if (fread(buffer, *size, 1, fp) != 1)
|
|
||||||
FATAL_ERROR("Failed to read \"%s\".\n", path);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount)
|
|
||||||
{
|
|
||||||
FILE *fp = fopen(path, "rb");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
|
|
||||||
*size = ftell(fp);
|
|
||||||
|
|
||||||
unsigned char *buffer = calloc(*size + padAmount, 1);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
|
|
||||||
|
|
||||||
rewind(fp);
|
|
||||||
|
|
||||||
if (fread(buffer, *size, 1, fp) != 1)
|
|
||||||
FATAL_ERROR("Failed to read \"%s\".\n", path);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteWholeFile(char *path, void *buffer, int bufferSize)
|
|
||||||
{
|
|
||||||
FILE *fp = fopen(path, "wb");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
|
|
||||||
|
|
||||||
if (fwrite(buffer, bufferSize, 1, fp) != 1)
|
|
||||||
FATAL_ERROR("Failed to write to \"%s\".\n", path);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright (c) 2015 YamaArashi
|
|
||||||
|
|
||||||
#ifndef UTIL_H
|
|
||||||
#define UTIL_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
bool ParseNumber(char *s, char **end, int radix, int *intValue);
|
|
||||||
char *GetFileExtension(char *path);
|
|
||||||
unsigned char *ReadWholeFile(char *path, int *size);
|
|
||||||
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount);
|
|
||||||
void WriteWholeFile(char *path, void *buffer, int bufferSize);
|
|
||||||
|
|
||||||
#endif // UTIL_H
|
|
|
@ -1 +0,0 @@
|
||||||
mid2agb
|
|
|
@ -1,18 +0,0 @@
|
||||||
CXX := g++
|
|
||||||
|
|
||||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
|
||||||
|
|
||||||
SRCS := agb.cpp error.cpp main.cpp midi.cpp tables.cpp
|
|
||||||
|
|
||||||
HEADERS := agb.h error.h main.h midi.h tables.h
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: mid2agb
|
|
||||||
@:
|
|
||||||
|
|
||||||
mid2agb: $(SRCS) $(HEADERS)
|
|
||||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) mid2agb mid2agb.exe
|
|
|
@ -1 +0,0 @@
|
||||||
preproc
|
|
|
@ -1,20 +0,0 @@
|
||||||
CXX := g++
|
|
||||||
|
|
||||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
|
||||||
|
|
||||||
SRCS := asm_file.cpp c_file.cpp charmap.cpp preproc.cpp string_parser.cpp \
|
|
||||||
utf8.cpp
|
|
||||||
|
|
||||||
HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \
|
|
||||||
utf8.h
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: preproc
|
|
||||||
@:
|
|
||||||
|
|
||||||
preproc: $(SRCS) $(HEADERS)
|
|
||||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) preproc preproc.exe
|
|
|
@ -1 +0,0 @@
|
||||||
scaninc
|
|
|
@ -1,18 +0,0 @@
|
||||||
CXX = g++
|
|
||||||
|
|
||||||
CXXFLAGS = -Wall -Werror -std=c++11 -O2
|
|
||||||
|
|
||||||
SRCS = scaninc.cpp c_file.cpp asm_file.cpp source_file.cpp
|
|
||||||
|
|
||||||
HEADERS := scaninc.h asm_file.h c_file.h source_file.h
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
all: scaninc
|
|
||||||
@:
|
|
||||||
|
|
||||||
scaninc: $(SRCS) $(HEADERS)
|
|
||||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) scaninc scaninc.exe
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# libraries
|
||||||
|
add_subdirectory(util)
|
||||||
|
# binaries
|
||||||
|
add_subdirectory(agb2mid)
|
||||||
|
add_subdirectory(aif2pcm)
|
||||||
|
add_subdirectory(asset_processor)
|
||||||
|
add_subdirectory(bin2c)
|
||||||
|
add_subdirectory(gbafix)
|
||||||
|
add_subdirectory(gbagfx)
|
||||||
|
add_subdirectory(mid2agb)
|
||||||
|
add_subdirectory(preproc)
|
||||||
|
add_subdirectory(scaninc)
|
||||||
|
add_subdirectory(tmc_strings)
|
|
@ -0,0 +1,6 @@
|
||||||
|
file(GLOB_RECURSE sources *.cpp)
|
||||||
|
|
||||||
|
add_executable(agb2mid ${sources})
|
||||||
|
target_include_directories(agb2mid PRIVATE .)
|
||||||
|
|
||||||
|
install(TARGETS agb2mid RUNTIME DESTINATION bin)
|
|
@ -0,0 +1,7 @@
|
||||||
|
file(GLOB_RECURSE sources *.c)
|
||||||
|
|
||||||
|
add_executable(aif2pcm ${sources})
|
||||||
|
target_include_directories(aif2pcm PRIVATE .)
|
||||||
|
target_link_libraries(aif2pcm PRIVATE m)
|
||||||
|
|
||||||
|
install(TARGETS aif2pcm RUNTIME DESTINATION bin)
|
|
@ -0,0 +1,164 @@
|
||||||
|
/* $Id: extended.c,v 1.8 2006/12/23 11:17:49 toad32767 Exp $ */
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any
|
||||||
|
* person obtaining a copy of this software and associated
|
||||||
|
* documentation files (the "Software"), to deal in the
|
||||||
|
* Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice
|
||||||
|
* shall be included in all copies or substantial portions of
|
||||||
|
* the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Infinite & NAN values
|
||||||
|
* for non-IEEE systems
|
||||||
|
*/
|
||||||
|
#ifndef HUGE_VAL
|
||||||
|
#ifdef HUGE
|
||||||
|
#define INFINITE_VALUE HUGE
|
||||||
|
#define NAN_VALUE HUGE
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define INFINITE_VALUE HUGE_VAL
|
||||||
|
#define NAN_VALUE HUGE_VAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IEEE 754 Extended Precision
|
||||||
|
*
|
||||||
|
* Implementation here is the 80-bit extended precision
|
||||||
|
* format of Motorola 68881, Motorola 68882 and Motorola
|
||||||
|
* 68040 FPUs, as well as Intel 80x87 FPUs.
|
||||||
|
*
|
||||||
|
* See:
|
||||||
|
* http://www.freescale.com/files/32bit/doc/fact_sheet/BR509.pdf
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Exponent range: [-16383,16383]
|
||||||
|
* Precision for mantissa: 64 bits with no hidden bit
|
||||||
|
* Bias: 16383
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write IEEE Extended Precision Numbers
|
||||||
|
*/
|
||||||
|
void ieee754_write_extended(double in, uint8_t* out) {
|
||||||
|
int sgn, exp, shift;
|
||||||
|
double fraction, t;
|
||||||
|
unsigned int lexp, hexp;
|
||||||
|
unsigned long low, high;
|
||||||
|
|
||||||
|
if (in == 0.0) {
|
||||||
|
memset(out, 0, 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (in < 0.0) {
|
||||||
|
in = fabs(in);
|
||||||
|
sgn = 1;
|
||||||
|
} else
|
||||||
|
sgn = 0;
|
||||||
|
|
||||||
|
fraction = frexp(in, &exp);
|
||||||
|
|
||||||
|
if (exp == 0 || exp > 16384) {
|
||||||
|
if (exp > 16384) /* infinite value */
|
||||||
|
low = high = 0;
|
||||||
|
else {
|
||||||
|
low = 0x80000000;
|
||||||
|
high = 0;
|
||||||
|
}
|
||||||
|
exp = 32767;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
fraction = ldexp(fraction, 32);
|
||||||
|
t = floor(fraction);
|
||||||
|
low = (unsigned long)t;
|
||||||
|
fraction -= t;
|
||||||
|
t = floor(ldexp(fraction, 32));
|
||||||
|
high = (unsigned long)t;
|
||||||
|
|
||||||
|
/* Convert exponents < -16382 to -16382 (then they will be
|
||||||
|
* stored as -16383) */
|
||||||
|
if (exp < -16382) {
|
||||||
|
shift = 0 - exp - 16382;
|
||||||
|
high >>= shift;
|
||||||
|
high |= (low << (32 - shift));
|
||||||
|
low >>= shift;
|
||||||
|
exp = -16382;
|
||||||
|
}
|
||||||
|
exp += 16383 - 1; /* bias */
|
||||||
|
|
||||||
|
done:
|
||||||
|
lexp = ((unsigned int)exp) >> 8;
|
||||||
|
hexp = ((unsigned int)exp) & 0xFF;
|
||||||
|
|
||||||
|
/* big endian */
|
||||||
|
out[0] = ((uint8_t)sgn) << 7;
|
||||||
|
out[0] |= (uint8_t)lexp;
|
||||||
|
out[1] = (uint8_t)hexp;
|
||||||
|
out[2] = (uint8_t)(low >> 24);
|
||||||
|
out[3] = (uint8_t)((low >> 16) & 0xFF);
|
||||||
|
out[4] = (uint8_t)((low >> 8) & 0xFF);
|
||||||
|
out[5] = (uint8_t)(low & 0xFF);
|
||||||
|
out[6] = (uint8_t)(high >> 24);
|
||||||
|
out[7] = (uint8_t)((high >> 16) & 0xFF);
|
||||||
|
out[8] = (uint8_t)((high >> 8) & 0xFF);
|
||||||
|
out[9] = (uint8_t)(high & 0xFF);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read IEEE Extended Precision Numbers
|
||||||
|
*/
|
||||||
|
double ieee754_read_extended(uint8_t* in) {
|
||||||
|
int sgn, exp;
|
||||||
|
unsigned long low, high;
|
||||||
|
double out;
|
||||||
|
|
||||||
|
/* Extract the components from the big endian buffer */
|
||||||
|
sgn = (int)(in[0] >> 7);
|
||||||
|
exp = ((int)(in[0] & 0x7F) << 8) | ((int)in[1]);
|
||||||
|
low = (((unsigned long)in[2]) << 24) | (((unsigned long)in[3]) << 16) | (((unsigned long)in[4]) << 8) |
|
||||||
|
(unsigned long)in[5];
|
||||||
|
high = (((unsigned long)in[6]) << 24) | (((unsigned long)in[7]) << 16) | (((unsigned long)in[8]) << 8) |
|
||||||
|
(unsigned long)in[9];
|
||||||
|
|
||||||
|
if (exp == 0 && low == 0 && high == 0)
|
||||||
|
return (sgn ? -0.0 : 0.0);
|
||||||
|
|
||||||
|
switch (exp) {
|
||||||
|
case 32767:
|
||||||
|
if (low == 0 && high == 0)
|
||||||
|
return (sgn ? -INFINITE_VALUE : INFINITE_VALUE);
|
||||||
|
else
|
||||||
|
return (sgn ? -NAN_VALUE : NAN_VALUE);
|
||||||
|
default:
|
||||||
|
exp -= 16383; /* unbias exponent */
|
||||||
|
}
|
||||||
|
|
||||||
|
out = ldexp((double)low, -31 + exp);
|
||||||
|
out += ldexp((double)high, -63 + exp);
|
||||||
|
|
||||||
|
return (sgn ? -out : out);
|
||||||
|
}
|
|
@ -0,0 +1,799 @@
|
||||||
|
// Copyright(c) 2016 huderlem
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* extended.c */
|
||||||
|
void ieee754_write_extended(double, uint8_t*);
|
||||||
|
double ieee754_read_extended(uint8_t*);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
#define FATAL_ERROR(format, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, format, __VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define FATAL_ERROR(format, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, format, ##__VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned long num_samples;
|
||||||
|
uint8_t* samples;
|
||||||
|
uint8_t midi_note;
|
||||||
|
bool has_loop;
|
||||||
|
unsigned long loop_offset;
|
||||||
|
double sample_rate;
|
||||||
|
unsigned long real_num_samples;
|
||||||
|
} AifData;
|
||||||
|
|
||||||
|
struct Bytes {
|
||||||
|
unsigned long length;
|
||||||
|
uint8_t* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Marker {
|
||||||
|
unsigned short id;
|
||||||
|
unsigned long position;
|
||||||
|
// don't care about the name
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bytes* read_bytearray(const char* filename) {
|
||||||
|
struct Bytes* bytes = malloc(sizeof(struct Bytes));
|
||||||
|
FILE* f = fopen(filename, "rb");
|
||||||
|
if (!f) {
|
||||||
|
FATAL_ERROR("Failed to open '%s' for reading!\n", filename);
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
bytes->length = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
bytes->data = malloc(bytes->length);
|
||||||
|
unsigned long read = fread(bytes->data, bytes->length, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
if (read <= 0) {
|
||||||
|
FATAL_ERROR("Failed to read data from '%s'!\n", filename);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_bytearray(const char* filename, struct Bytes* bytes) {
|
||||||
|
FILE* f = fopen(filename, "wb");
|
||||||
|
if (!f) {
|
||||||
|
FATAL_ERROR("Failed to open '%s' for writing!\n", filename);
|
||||||
|
}
|
||||||
|
fwrite(bytes->data, bytes->length, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_bytearray(struct Bytes* bytes) {
|
||||||
|
free(bytes->data);
|
||||||
|
free(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_file_extension(char* filename) {
|
||||||
|
char* index = strrchr(filename, '.');
|
||||||
|
if (!index || index == filename) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* new_file_extension(char* filename, char* ext) {
|
||||||
|
char* index = strrchr(filename, '.');
|
||||||
|
if (!index || index == filename) {
|
||||||
|
index = filename + strlen(filename);
|
||||||
|
}
|
||||||
|
int length = index - filename;
|
||||||
|
char* new_filename = malloc(length + 1 + strlen(ext) + 1);
|
||||||
|
if (new_filename) {
|
||||||
|
strcpy(new_filename, filename);
|
||||||
|
new_filename[length] = '.';
|
||||||
|
strcpy(new_filename + length + 1, ext);
|
||||||
|
}
|
||||||
|
return new_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_aif(struct Bytes* aif, AifData* aif_data) {
|
||||||
|
aif_data->has_loop = false;
|
||||||
|
aif_data->num_samples = 0;
|
||||||
|
|
||||||
|
unsigned long pos = 0;
|
||||||
|
char chunk_name[5];
|
||||||
|
chunk_name[4] = '\0';
|
||||||
|
char chunk_type[5];
|
||||||
|
chunk_type[4] = '\0';
|
||||||
|
|
||||||
|
// Check for FORM Chunk
|
||||||
|
memcpy(chunk_name, &aif->data[pos], 4);
|
||||||
|
pos += 4;
|
||||||
|
if (strcmp(chunk_name, "FORM") != 0) {
|
||||||
|
FATAL_ERROR("Input .aif file has invalid header Chunk '%s'!\n", chunk_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read size of whole file.
|
||||||
|
unsigned long whole_chunk_size = aif->data[pos++] << 24;
|
||||||
|
whole_chunk_size |= (aif->data[pos++] << 16);
|
||||||
|
whole_chunk_size |= (aif->data[pos++] << 8);
|
||||||
|
whole_chunk_size |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
unsigned long expected_whole_chunk_size = aif->length - 8;
|
||||||
|
if (whole_chunk_size != expected_whole_chunk_size) {
|
||||||
|
FATAL_ERROR("FORM Chunk ckSize '%lu' doesn't match actual size '%lu'!\n", whole_chunk_size,
|
||||||
|
expected_whole_chunk_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for AIFF Form Type
|
||||||
|
memcpy(chunk_type, &aif->data[pos], 4);
|
||||||
|
pos += 4;
|
||||||
|
if (strcmp(chunk_type, "AIFF") != 0) {
|
||||||
|
FATAL_ERROR("FORM Type is '%s', but it must be AIFF!", chunk_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Marker* markers = NULL;
|
||||||
|
unsigned short num_markers = 0, loop_start = 0, loop_end = 0;
|
||||||
|
unsigned long num_sample_frames = 0;
|
||||||
|
|
||||||
|
// Read all the Chunks to populate the AifData struct.
|
||||||
|
while ((pos + 8) < aif->length) {
|
||||||
|
// Read Chunk id
|
||||||
|
memcpy(chunk_name, &aif->data[pos], 4);
|
||||||
|
pos += 4;
|
||||||
|
|
||||||
|
unsigned long chunk_size = (aif->data[pos++] << 24);
|
||||||
|
chunk_size |= (aif->data[pos++] << 16);
|
||||||
|
chunk_size |= (aif->data[pos++] << 8);
|
||||||
|
chunk_size |= aif->data[pos++];
|
||||||
|
|
||||||
|
if ((pos + chunk_size) > aif->length) {
|
||||||
|
FATAL_ERROR("%s chunk at 0x%lx reached end of file before finishing\n", chunk_name, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(chunk_name, "COMM") == 0) {
|
||||||
|
short num_channels = (aif->data[pos++] << 8);
|
||||||
|
num_channels |= (uint8_t)aif->data[pos++];
|
||||||
|
if (num_channels != 1) {
|
||||||
|
FATAL_ERROR("numChannels (%d) in the COMM Chunk must be 1!\n", num_channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
num_sample_frames = (aif->data[pos++] << 24);
|
||||||
|
num_sample_frames |= (aif->data[pos++] << 16);
|
||||||
|
num_sample_frames |= (aif->data[pos++] << 8);
|
||||||
|
num_sample_frames |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
short sample_size = (aif->data[pos++] << 8);
|
||||||
|
sample_size |= (uint8_t)aif->data[pos++];
|
||||||
|
if (sample_size != 8) {
|
||||||
|
FATAL_ERROR("sampleSize (%d) in the COMM Chunk must be 8!\n", sample_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
double sample_rate = ieee754_read_extended((uint8_t*)(aif->data + pos));
|
||||||
|
pos += 10;
|
||||||
|
|
||||||
|
aif_data->sample_rate = sample_rate;
|
||||||
|
|
||||||
|
if (aif_data->num_samples == 0) {
|
||||||
|
aif_data->num_samples = num_sample_frames;
|
||||||
|
}
|
||||||
|
} else if (strcmp(chunk_name, "MARK") == 0) {
|
||||||
|
num_markers = (aif->data[pos++] << 8);
|
||||||
|
num_markers |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
if (markers) {
|
||||||
|
FATAL_ERROR("More than one MARK Chunk in file!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
markers = calloc(num_markers, sizeof(struct Marker));
|
||||||
|
|
||||||
|
// Read each marker.
|
||||||
|
for (int i = 0; i < num_markers; i++) {
|
||||||
|
unsigned short marker_id = (aif->data[pos++] << 8);
|
||||||
|
marker_id |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
unsigned long marker_position = (aif->data[pos++] << 24);
|
||||||
|
marker_position |= (aif->data[pos++] << 16);
|
||||||
|
marker_position |= (aif->data[pos++] << 8);
|
||||||
|
marker_position |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
// Marker name is a Pascal-style string.
|
||||||
|
uint8_t marker_name_size = aif->data[pos++];
|
||||||
|
// We don't actually need the marker name for anything anymore.
|
||||||
|
/*char *marker_name = (char *)malloc((marker_name_size + 1) * sizeof(char));
|
||||||
|
memcpy(marker_name, &aif->data[pos], marker_name_size);
|
||||||
|
marker_name[marker_name_size] = '\0';*/
|
||||||
|
pos += marker_name_size + !(marker_name_size & 1);
|
||||||
|
|
||||||
|
markers[i].id = marker_id;
|
||||||
|
markers[i].position = marker_position;
|
||||||
|
}
|
||||||
|
} else if (strcmp(chunk_name, "INST") == 0) {
|
||||||
|
uint8_t midi_note = (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
aif_data->midi_note = midi_note;
|
||||||
|
|
||||||
|
// Skip over data we don't need.
|
||||||
|
pos += 7;
|
||||||
|
|
||||||
|
unsigned short loop_type = (aif->data[pos++] << 8);
|
||||||
|
loop_type |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
if (loop_type) {
|
||||||
|
loop_start = (aif->data[pos++] << 8);
|
||||||
|
loop_start |= (uint8_t)aif->data[pos++];
|
||||||
|
|
||||||
|
loop_end = (aif->data[pos++] << 8);
|
||||||
|
loop_end |= (uint8_t)aif->data[pos++];
|
||||||
|
} else {
|
||||||
|
// Skip NoLooping sustain loop.
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip release loop, we don't need it.
|
||||||
|
pos += 6;
|
||||||
|
} else if (strcmp(chunk_name, "SSND") == 0) {
|
||||||
|
// Skip offset and blockSize
|
||||||
|
pos += 8;
|
||||||
|
|
||||||
|
unsigned long num_samples = chunk_size - 8;
|
||||||
|
uint8_t* sample_data = (uint8_t*)malloc(num_samples * sizeof(uint8_t));
|
||||||
|
memcpy(sample_data, &aif->data[pos], num_samples);
|
||||||
|
|
||||||
|
aif_data->samples = sample_data;
|
||||||
|
aif_data->real_num_samples = num_samples;
|
||||||
|
pos += chunk_size - 8;
|
||||||
|
} else {
|
||||||
|
// Skip over unsupported chunks.
|
||||||
|
pos += chunk_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markers) {
|
||||||
|
// Resolve loop points.
|
||||||
|
struct Marker* cur_marker = markers;
|
||||||
|
|
||||||
|
// Grab loop start point.
|
||||||
|
for (int i = 0; i < num_markers; i++, cur_marker++) {
|
||||||
|
if (cur_marker->id == loop_start) {
|
||||||
|
aif_data->loop_offset = cur_marker->position;
|
||||||
|
aif_data->has_loop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_marker = markers;
|
||||||
|
|
||||||
|
// Grab loop end point.
|
||||||
|
for (int i = 0; i < num_markers; i++, cur_marker++) {
|
||||||
|
if (cur_marker->id == loop_end) {
|
||||||
|
if (cur_marker->position < aif_data->loop_offset) {
|
||||||
|
aif_data->loop_offset = cur_marker->position;
|
||||||
|
aif_data->has_loop = true;
|
||||||
|
}
|
||||||
|
aif_data->num_samples = cur_marker->position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(markers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a table of deltas between sample values in compressed PCM data.
|
||||||
|
const int gDeltaEncodingTable[] = {
|
||||||
|
0, 1, 4, 9, 16, 25, 36, 49, -64, -49, -36, -25, -16, -9, -4, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bytes* delta_decompress(struct Bytes* delta, unsigned int expected_length) {
|
||||||
|
struct Bytes* pcm = malloc(sizeof(struct Bytes));
|
||||||
|
pcm->length = expected_length;
|
||||||
|
pcm->data = malloc(pcm->length + 0x40);
|
||||||
|
|
||||||
|
uint8_t hi, lo;
|
||||||
|
unsigned int i = 0;
|
||||||
|
unsigned int j = 0;
|
||||||
|
int k;
|
||||||
|
int8_t base;
|
||||||
|
while (i < delta->length) {
|
||||||
|
base = (int8_t)delta->data[i++];
|
||||||
|
pcm->data[j++] = (uint8_t)base;
|
||||||
|
if (i >= delta->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lo = delta->data[i] & 0xf;
|
||||||
|
base += gDeltaEncodingTable[lo];
|
||||||
|
pcm->data[j++] = base;
|
||||||
|
i++;
|
||||||
|
if (i >= delta->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (k = 0; k < 31; k++) {
|
||||||
|
hi = (delta->data[i] >> 4) & 0xf;
|
||||||
|
base += gDeltaEncodingTable[hi];
|
||||||
|
pcm->data[j++] = base;
|
||||||
|
if (j >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lo = delta->data[i] & 0xf;
|
||||||
|
base += gDeltaEncodingTable[lo];
|
||||||
|
pcm->data[j++] = base;
|
||||||
|
i++;
|
||||||
|
if (i >= delta->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (j >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pcm->length = j;
|
||||||
|
return pcm;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_delta_index(uint8_t sample, uint8_t prev_sample) {
|
||||||
|
int best_error = INT_MAX;
|
||||||
|
int best_index = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
uint8_t new_sample = prev_sample + gDeltaEncodingTable[i];
|
||||||
|
int error = sample > new_sample ? sample - new_sample : new_sample - sample;
|
||||||
|
|
||||||
|
if (error < best_error) {
|
||||||
|
best_error = error;
|
||||||
|
best_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bytes* delta_compress(struct Bytes* pcm) {
|
||||||
|
struct Bytes* delta = malloc(sizeof(struct Bytes));
|
||||||
|
// estimate the length so we can malloc
|
||||||
|
int num_blocks = pcm->length / 64;
|
||||||
|
delta->length = num_blocks * 33;
|
||||||
|
|
||||||
|
int extra = pcm->length % 64;
|
||||||
|
if (extra) {
|
||||||
|
delta->length += 1;
|
||||||
|
extra -= 1;
|
||||||
|
}
|
||||||
|
if (extra) {
|
||||||
|
delta->length += 1;
|
||||||
|
extra -= 1;
|
||||||
|
}
|
||||||
|
if (extra) {
|
||||||
|
delta->length += (extra + 1) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta->data = malloc(delta->length + 33);
|
||||||
|
|
||||||
|
unsigned int i = 0;
|
||||||
|
unsigned int j = 0;
|
||||||
|
int k;
|
||||||
|
uint8_t base;
|
||||||
|
int delta_index;
|
||||||
|
|
||||||
|
while (i < pcm->length) {
|
||||||
|
base = pcm->data[i++];
|
||||||
|
delta->data[j++] = base;
|
||||||
|
|
||||||
|
if (i >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delta_index = get_delta_index(pcm->data[i++], base);
|
||||||
|
base += gDeltaEncodingTable[delta_index];
|
||||||
|
delta->data[j++] = delta_index;
|
||||||
|
|
||||||
|
for (k = 0; k < 31; k++) {
|
||||||
|
if (i >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delta_index = get_delta_index(pcm->data[i++], base);
|
||||||
|
base += gDeltaEncodingTable[delta_index];
|
||||||
|
delta->data[j] = (delta_index << 4);
|
||||||
|
|
||||||
|
if (i >= pcm->length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delta_index = get_delta_index(pcm->data[i++], base);
|
||||||
|
base += gDeltaEncodingTable[delta_index];
|
||||||
|
delta->data[j++] |= delta_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delta->length = j;
|
||||||
|
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STORE_U32_LE(dest, value) \
|
||||||
|
do { \
|
||||||
|
*(dest) = (value)&0xff; \
|
||||||
|
*((dest) + 1) = ((value) >> 8) & 0xff; \
|
||||||
|
*((dest) + 2) = ((value) >> 16) & 0xff; \
|
||||||
|
*((dest) + 3) = ((value) >> 24) & 0xff; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOAD_U32_LE(var, src) \
|
||||||
|
do { \
|
||||||
|
(var) = *(src); \
|
||||||
|
(var) |= (*((src) + 1) << 8); \
|
||||||
|
(var) |= (*((src) + 2) << 16); \
|
||||||
|
(var) |= (*((src) + 3) << 24); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// Reads an .aif file and produces a .pcm file containing an array of 8-bit samples.
|
||||||
|
void aif2pcm(const char* aif_filename, const char* pcm_filename, bool compress) {
|
||||||
|
struct Bytes* aif = read_bytearray(aif_filename);
|
||||||
|
AifData aif_data = { 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
read_aif(aif, &aif_data);
|
||||||
|
|
||||||
|
int header_size = 0x10;
|
||||||
|
struct Bytes* pcm;
|
||||||
|
struct Bytes output = { 0, 0 };
|
||||||
|
|
||||||
|
if (compress) {
|
||||||
|
struct Bytes* input = malloc(sizeof(struct Bytes));
|
||||||
|
input->data = aif_data.samples;
|
||||||
|
input->length = aif_data.real_num_samples;
|
||||||
|
pcm = delta_compress(input);
|
||||||
|
free(input);
|
||||||
|
} else {
|
||||||
|
pcm = malloc(sizeof(struct Bytes));
|
||||||
|
pcm->data = aif_data.samples;
|
||||||
|
pcm->length = aif_data.real_num_samples;
|
||||||
|
}
|
||||||
|
output.length = header_size + pcm->length;
|
||||||
|
output.data = malloc(output.length);
|
||||||
|
|
||||||
|
uint32_t pitch_adjust = (uint32_t)(aif_data.sample_rate * 1024);
|
||||||
|
uint32_t loop_offset = (uint32_t)(aif_data.loop_offset);
|
||||||
|
uint32_t adjusted_num_samples = (uint32_t)(aif_data.num_samples - 1);
|
||||||
|
uint32_t flags = 0;
|
||||||
|
if (aif_data.has_loop)
|
||||||
|
flags |= 0x40000000;
|
||||||
|
if (compress)
|
||||||
|
flags |= 1;
|
||||||
|
STORE_U32_LE(output.data + 0, flags);
|
||||||
|
STORE_U32_LE(output.data + 4, pitch_adjust);
|
||||||
|
STORE_U32_LE(output.data + 8, loop_offset);
|
||||||
|
STORE_U32_LE(output.data + 12, adjusted_num_samples);
|
||||||
|
memcpy(&output.data[header_size], pcm->data, pcm->length);
|
||||||
|
write_bytearray(pcm_filename, &output);
|
||||||
|
|
||||||
|
free(aif->data);
|
||||||
|
free(aif);
|
||||||
|
free(pcm);
|
||||||
|
free(output.data);
|
||||||
|
free(aif_data.samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a .pcm file containing an array of 8-bit samples and produces an .aif file.
|
||||||
|
// See http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-1.3.pdf for .aif file specification.
|
||||||
|
void pcm2aif(const char* pcm_filename, const char* aif_filename, uint32_t base_note) {
|
||||||
|
struct Bytes* pcm = read_bytearray(pcm_filename);
|
||||||
|
|
||||||
|
AifData* aif_data = malloc(sizeof(AifData));
|
||||||
|
|
||||||
|
uint32_t flags;
|
||||||
|
LOAD_U32_LE(flags, pcm->data + 0);
|
||||||
|
aif_data->has_loop = flags & 0x40000000;
|
||||||
|
bool compressed = flags & 1;
|
||||||
|
|
||||||
|
uint32_t pitch_adjust;
|
||||||
|
LOAD_U32_LE(pitch_adjust, pcm->data + 4);
|
||||||
|
aif_data->sample_rate = pitch_adjust / 1024.0;
|
||||||
|
|
||||||
|
LOAD_U32_LE(aif_data->loop_offset, pcm->data + 8);
|
||||||
|
LOAD_U32_LE(aif_data->num_samples, pcm->data + 12);
|
||||||
|
aif_data->num_samples += 1;
|
||||||
|
|
||||||
|
if (compressed) {
|
||||||
|
struct Bytes* delta = pcm;
|
||||||
|
uint8_t* pcm_data = pcm->data;
|
||||||
|
delta->length -= 0x10;
|
||||||
|
delta->data += 0x10;
|
||||||
|
pcm = delta_decompress(delta, aif_data->num_samples);
|
||||||
|
free(pcm_data);
|
||||||
|
free(delta);
|
||||||
|
} else {
|
||||||
|
pcm->length -= 0x10;
|
||||||
|
pcm->data += 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
aif_data->samples = malloc(pcm->length);
|
||||||
|
memcpy(aif_data->samples, pcm->data, pcm->length);
|
||||||
|
|
||||||
|
struct Bytes* aif = malloc(sizeof(struct Bytes));
|
||||||
|
aif->length = 54 + 60 + pcm->length;
|
||||||
|
aif->data = malloc(aif->length);
|
||||||
|
|
||||||
|
long pos = 0;
|
||||||
|
|
||||||
|
// First, write the FORM header chunk.
|
||||||
|
// FORM Chunk ckID
|
||||||
|
aif->data[pos++] = 'F';
|
||||||
|
aif->data[pos++] = 'O';
|
||||||
|
aif->data[pos++] = 'R';
|
||||||
|
aif->data[pos++] = 'M';
|
||||||
|
|
||||||
|
// FORM Chunk ckSize
|
||||||
|
unsigned long form_size = pos;
|
||||||
|
unsigned long data_size = aif->length - 8;
|
||||||
|
aif->data[pos++] = ((data_size >> 24) & 0xFF);
|
||||||
|
aif->data[pos++] = ((data_size >> 16) & 0xFF);
|
||||||
|
aif->data[pos++] = ((data_size >> 8) & 0xFF);
|
||||||
|
aif->data[pos++] = (data_size & 0xFF);
|
||||||
|
|
||||||
|
// FORM Chunk formType
|
||||||
|
aif->data[pos++] = 'A';
|
||||||
|
aif->data[pos++] = 'I';
|
||||||
|
aif->data[pos++] = 'F';
|
||||||
|
aif->data[pos++] = 'F';
|
||||||
|
|
||||||
|
// Next, write the Common Chunk
|
||||||
|
// Common Chunk ckID
|
||||||
|
aif->data[pos++] = 'C';
|
||||||
|
aif->data[pos++] = 'O';
|
||||||
|
aif->data[pos++] = 'M';
|
||||||
|
aif->data[pos++] = 'M';
|
||||||
|
|
||||||
|
// Common Chunk ckSize
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 18;
|
||||||
|
|
||||||
|
// Common Chunk numChannels
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 1; // 1 channel
|
||||||
|
|
||||||
|
// Common Chunk numSampleFrames
|
||||||
|
aif->data[pos++] = ((aif_data->num_samples >> 24) & 0xFF);
|
||||||
|
aif->data[pos++] = ((aif_data->num_samples >> 16) & 0xFF);
|
||||||
|
aif->data[pos++] = ((aif_data->num_samples >> 8) & 0xFF);
|
||||||
|
aif->data[pos++] = (aif_data->num_samples & 0xFF);
|
||||||
|
|
||||||
|
// Common Chunk sampleSize
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 8; // 8 bits per sample
|
||||||
|
|
||||||
|
// Common Chunk sampleRate
|
||||||
|
// double sample_rate = pitch_adjust / 1024.0;
|
||||||
|
uint8_t sample_rate_buffer[10];
|
||||||
|
ieee754_write_extended(aif_data->sample_rate, sample_rate_buffer);
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
aif->data[pos++] = sample_rate_buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aif_data->has_loop) {
|
||||||
|
|
||||||
|
// Marker Chunk ckID
|
||||||
|
aif->data[pos++] = 'M';
|
||||||
|
aif->data[pos++] = 'A';
|
||||||
|
aif->data[pos++] = 'R';
|
||||||
|
aif->data[pos++] = 'K';
|
||||||
|
|
||||||
|
// Marker Chunk ckSize
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 12 + (aif_data->has_loop ? 12 : 0);
|
||||||
|
|
||||||
|
// Marker Chunk numMarkers
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = (aif_data->has_loop ? 2 : 1);
|
||||||
|
|
||||||
|
// Marker loop start
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 1; // id = 1
|
||||||
|
|
||||||
|
long loop_start = aif_data->loop_offset;
|
||||||
|
aif->data[pos++] = ((loop_start >> 24) & 0xFF);
|
||||||
|
aif->data[pos++] = ((loop_start >> 16) & 0xFF);
|
||||||
|
aif->data[pos++] = ((loop_start >> 8) & 0xFF);
|
||||||
|
aif->data[pos++] = (loop_start & 0xFF); // position
|
||||||
|
|
||||||
|
aif->data[pos++] = 5; // pascal-style string length
|
||||||
|
aif->data[pos++] = 'S';
|
||||||
|
aif->data[pos++] = 'T';
|
||||||
|
aif->data[pos++] = 'A';
|
||||||
|
aif->data[pos++] = 'R';
|
||||||
|
aif->data[pos++] = 'T'; // markerName
|
||||||
|
|
||||||
|
// Marker loop end
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = (aif_data->has_loop ? 2 : 1); // id = 2
|
||||||
|
|
||||||
|
long loop_end = aif_data->num_samples;
|
||||||
|
aif->data[pos++] = ((loop_end >> 24) & 0xFF);
|
||||||
|
aif->data[pos++] = ((loop_end >> 16) & 0xFF);
|
||||||
|
aif->data[pos++] = ((loop_end >> 8) & 0xFF);
|
||||||
|
aif->data[pos++] = (loop_end & 0xFF); // position
|
||||||
|
|
||||||
|
aif->data[pos++] = 3; // pascal-style string length
|
||||||
|
aif->data[pos++] = 'E';
|
||||||
|
aif->data[pos++] = 'N';
|
||||||
|
aif->data[pos++] = 'D';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instrument Chunk ckID
|
||||||
|
aif->data[pos++] = 'I';
|
||||||
|
aif->data[pos++] = 'N';
|
||||||
|
aif->data[pos++] = 'S';
|
||||||
|
aif->data[pos++] = 'T';
|
||||||
|
|
||||||
|
// Instrument Chunk ckSize
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 20;
|
||||||
|
|
||||||
|
aif->data[pos++] = base_note; // baseNote
|
||||||
|
aif->data[pos++] = 0; // detune
|
||||||
|
aif->data[pos++] = 0; // lowNote
|
||||||
|
aif->data[pos++] = 127; // highNote
|
||||||
|
aif->data[pos++] = 1; // lowVelocity
|
||||||
|
aif->data[pos++] = 127; // highVelocity
|
||||||
|
aif->data[pos++] = 0; // gain (hi)
|
||||||
|
aif->data[pos++] = 0; // gain (lo)
|
||||||
|
|
||||||
|
// Instrument Chunk sustainLoop
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 1; // playMode = ForwardLooping
|
||||||
|
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 1; // beginLoop marker id
|
||||||
|
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 2; // endLoop marker id
|
||||||
|
|
||||||
|
// Instrument Chunk releaseLoop
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 1; // playMode = ForwardLooping
|
||||||
|
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 1; // beginLoop marker id
|
||||||
|
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 2; // endLoop marker id
|
||||||
|
|
||||||
|
// Finally, write the Sound Data Chunk
|
||||||
|
// Sound Data Chunk ckID
|
||||||
|
aif->data[pos++] = 'S';
|
||||||
|
aif->data[pos++] = 'S';
|
||||||
|
aif->data[pos++] = 'N';
|
||||||
|
aif->data[pos++] = 'D';
|
||||||
|
|
||||||
|
// Sound Data Chunk ckSize
|
||||||
|
unsigned long sound_data_size = pcm->length + 8;
|
||||||
|
aif->data[pos++] = ((sound_data_size >> 24) & 0xFF);
|
||||||
|
aif->data[pos++] = ((sound_data_size >> 16) & 0xFF);
|
||||||
|
aif->data[pos++] = ((sound_data_size >> 8) & 0xFF);
|
||||||
|
aif->data[pos++] = (sound_data_size & 0xFF);
|
||||||
|
|
||||||
|
// Sound Data Chunk offset
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
|
||||||
|
// Sound Data Chunk blockSize
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
aif->data[pos++] = 0;
|
||||||
|
|
||||||
|
// Sound Data Chunk soundData
|
||||||
|
for (unsigned int i = 0; i < aif_data->loop_offset; i++) {
|
||||||
|
aif->data[pos++] = aif_data->samples[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (unsigned int i = aif_data->loop_offset; i < pcm->length; i++) {
|
||||||
|
int pcm_index = aif_data->loop_offset + (j++ % (pcm->length - aif_data->loop_offset));
|
||||||
|
aif->data[pos++] = aif_data->samples[pcm_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
aif->length = pos;
|
||||||
|
|
||||||
|
// Go back and rewrite ckSize
|
||||||
|
data_size = aif->length - 8;
|
||||||
|
aif->data[form_size + 0] = ((data_size >> 24) & 0xFF);
|
||||||
|
aif->data[form_size + 1] = ((data_size >> 16) & 0xFF);
|
||||||
|
aif->data[form_size + 2] = ((data_size >> 8) & 0xFF);
|
||||||
|
aif->data[form_size + 3] = (data_size & 0xFF);
|
||||||
|
|
||||||
|
write_bytearray(aif_filename, aif);
|
||||||
|
|
||||||
|
free(aif->data);
|
||||||
|
free(aif);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(void) {
|
||||||
|
fprintf(stderr, "Usage: aif2pcm bin_file [aif_file]\n");
|
||||||
|
fprintf(stderr, " aif2pcm aif_file [bin_file] [--compress]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
usage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* input_file = argv[1];
|
||||||
|
char* extension = get_file_extension(input_file);
|
||||||
|
char* output_file;
|
||||||
|
bool compressed = false;
|
||||||
|
|
||||||
|
if (argc > 3) {
|
||||||
|
for (int i = 3; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--compress") == 0) {
|
||||||
|
compressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(extension, "aif") == 0 || strcmp(extension, "aiff") == 0) {
|
||||||
|
if (argc >= 3) {
|
||||||
|
output_file = argv[2];
|
||||||
|
aif2pcm(input_file, output_file, compressed);
|
||||||
|
} else {
|
||||||
|
output_file = new_file_extension(input_file, "bin");
|
||||||
|
aif2pcm(input_file, output_file, compressed);
|
||||||
|
free(output_file);
|
||||||
|
}
|
||||||
|
} else if (strcmp(extension, "bin") == 0) {
|
||||||
|
if (argc >= 3) {
|
||||||
|
output_file = argv[2];
|
||||||
|
pcm2aif(input_file, output_file, 60);
|
||||||
|
} else {
|
||||||
|
output_file = new_file_extension(input_file, "aif");
|
||||||
|
pcm2aif(input_file, output_file, 60);
|
||||||
|
free(output_file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FATAL_ERROR("Input file must be .aif or .bin: '%s'\n", input_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
file(GLOB_RECURSE sources *.cpp)
|
||||||
|
|
||||||
|
add_executable(asset_processor ${sources})
|
||||||
|
target_include_directories(asset_processor PRIVATE .)
|
||||||
|
target_link_libraries(asset_processor PRIVATE project_settings)
|
||||||
|
target_link_libraries(asset_processor PRIVATE fmt::fmt nlohmann_json::nlohmann_json filesystem util)
|
||||||
|
|
||||||
|
install(TARGETS asset_processor RUNTIME DESTINATION bin)
|
|
@ -2,8 +2,8 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
std::filesystem::path AifAsset::generateAssetPath() {
|
std::filesystem::path AifAsset::generateAssetPath() {
|
||||||
std::filesystem::path path = this->path;
|
std::filesystem::path asset_path = path;
|
||||||
return path.replace_extension(".aif");
|
return asset_path.replace_extension(".aif");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AifAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
void AifAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
@ -11,17 +11,17 @@ void AifAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "aif2pcm" / "aif2pcm");
|
cmd.push_back(toolsPath / "bin" / "aif2pcm");
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AifAsset::buildToBinary() {
|
void AifAsset::buildToBinary() {
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "aif2pcm" / "aif2pcm");
|
cmd.push_back(toolsPath / "bin" / "aif2pcm");
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "animation.h"
|
||||||
|
#include "reader.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <util/file.h>
|
||||||
|
|
||||||
|
void AnimationAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
Reader reader(baserom, start, size);
|
||||||
|
bool end_of_animation = false;
|
||||||
|
auto file = util::open_file(assetPath, "w");
|
||||||
|
while (!end_of_animation && reader.cursor + 3 < size) {
|
||||||
|
u8 frame_index = reader.read_u8();
|
||||||
|
u8 keyframe_duration = reader.read_u8();
|
||||||
|
u8 bitfield = reader.read_u8();
|
||||||
|
u8 bitfield2 = reader.read_u8();
|
||||||
|
end_of_animation = (bitfield2 & 0x80) != 0;
|
||||||
|
auto line = fmt::format("\tkeyframe frame_index={}", frame_index);
|
||||||
|
line += opt_param(", duration={}", 0, keyframe_duration);
|
||||||
|
line += opt_param(", bitfield={:#x}", 0, bitfield);
|
||||||
|
line += opt_param(", bitfield2={:#x}", 0, bitfield2);
|
||||||
|
std::fputs(line.c_str(), file.get());
|
||||||
|
std::fputc('\n', file.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!end_of_animation) {
|
||||||
|
std::fputs("@ TODO why no terminator?\n", file.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader.cursor < size) {
|
||||||
|
u8 keyframe_count = reader.read_u8();
|
||||||
|
fmt::print(file.get(), "\t.byte {} @ keyframe count\n", keyframe_count);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "asset.h"
|
||||||
|
#include <util/file.h>
|
||||||
|
|
||||||
|
void BaseAsset::extractBinary(const std::vector<char>& baserom) {
|
||||||
|
auto file = util::open_file(path, "w");
|
||||||
|
std::fwrite(baserom.data() + start, 1, static_cast<size_t>(size), file.get());
|
||||||
|
}
|
|
@ -3,30 +3,30 @@
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
class BaseAsset {
|
class BaseAsset {
|
||||||
public:
|
public:
|
||||||
BaseAsset(std::filesystem::path path, int start, int size, const nlohmann::json& asset)
|
BaseAsset(std::filesystem::path path_, int start_, int size_, const nlohmann::json& asset_)
|
||||||
: path(path), start(start), size(size), asset(asset) {
|
: path(std::move(path_)), start(start_), size(size_), asset(asset_) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~BaseAsset() = default;
|
virtual ~BaseAsset() = default;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// Cannot call virtual functions in constructor, so another function call is necessary
|
// Cannot call virtual functions in constructor, so another function call is necessary
|
||||||
assetPath = this->generateAssetPath();
|
assetPath = generateAssetPath();
|
||||||
buildPath = this->generateBuildPath();
|
buildPath = generateBuildPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the binary segment for this asset from the baserom and store it in a separate file.
|
// Extract the binary segment for this asset from the baserom and store it in a separate file.
|
||||||
virtual void extractBinary(const std::vector<char>& baserom);
|
virtual void extractBinary(const std::vector<char>& baserom);
|
||||||
|
|
||||||
// Convert the binary data for this asset to a human readable form.
|
// Convert the binary data for this asset to a human readable form.
|
||||||
virtual void convertToHumanReadable(const std::vector<char>& baserom) {
|
virtual void convertToHumanReadable([[maybe_unused]] const std::vector<char>& baserom) {
|
||||||
(void)baserom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the asset from the human readable form back to the binary.
|
// Build the asset from the human readable form back to the binary.
|
||||||
|
@ -34,30 +34,31 @@ class BaseAsset {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the path to the binary file extracted from the baserom.
|
// Returns the path to the binary file extracted from the baserom.
|
||||||
std::filesystem::path getPath() const {
|
[[nodiscard]] std::filesystem::path getPath() const {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the path to the human readable asset file.
|
// Returns the path to the human readable asset file.
|
||||||
std::filesystem::path getAssetPath() const {
|
[[nodiscard]] std::filesystem::path getAssetPath() const {
|
||||||
return assetPath;
|
return assetPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the path to the resulting file after building.
|
// Returns the path to the resulting file after building.
|
||||||
std::filesystem::path getBuildPath() const {
|
[[nodiscard]] std::filesystem::path getBuildPath() const {
|
||||||
return buildPath;
|
return buildPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the base of the filename of the asset.
|
// Returns the base of the filename of the asset.
|
||||||
std::string getSymbol() const {
|
[[nodiscard]] std::string getSymbol() const {
|
||||||
// Need to get the stem twice to remove both of the .4bpp.lz extensions.
|
// Need to get the stem twice to remove both of the .4bpp.lz extensions.
|
||||||
return (this->path.stem()).stem();
|
return (path.stem()).stem();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the start address of this asset.
|
// Returns the start address of this asset.
|
||||||
int getStart() const {
|
[[nodiscard]] int getStart() const {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::filesystem::path path;
|
std::filesystem::path path;
|
||||||
std::filesystem::path assetPath;
|
std::filesystem::path assetPath;
|
||||||
|
@ -68,10 +69,10 @@ class BaseAsset {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual std::filesystem::path generateAssetPath() {
|
virtual std::filesystem::path generateAssetPath() {
|
||||||
return this->path;
|
return path;
|
||||||
}
|
}
|
||||||
virtual std::filesystem::path generateBuildPath() {
|
virtual std::filesystem::path generateBuildPath() {
|
||||||
return this->path;
|
return path;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include "exitlist.h"
|
||||||
|
#include "reader.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <util/file.h>
|
||||||
|
|
||||||
|
void ExitListAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
Reader reader(baserom, start, size);
|
||||||
|
auto file = util::open_file(assetPath, "w");
|
||||||
|
while (reader.cursor < size) {
|
||||||
|
u16 transition_type = reader.read_u16();
|
||||||
|
u16 x_pos = reader.read_u16();
|
||||||
|
u16 y_pos = reader.read_u16();
|
||||||
|
u16 dest_x = reader.read_u16();
|
||||||
|
u16 dest_y = reader.read_u16();
|
||||||
|
u8 screen_edge = reader.read_u8();
|
||||||
|
u8 dest_area = reader.read_u8();
|
||||||
|
u8 dest_room = reader.read_u8();
|
||||||
|
u8 unknown_2 = reader.read_u8();
|
||||||
|
u8 unknown_3 = reader.read_u8();
|
||||||
|
u8 unknown_4 = reader.read_u8();
|
||||||
|
u16 unknown_5 = reader.read_u16();
|
||||||
|
u16 padding_1 = reader.read_u16();
|
||||||
|
if (transition_type == 0xffff) {
|
||||||
|
std::fputs("\texit_list_end\n", file.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto line = fmt::format("\texit transition={}", transition_type);
|
||||||
|
line += opt_param(", x={:#x}", 0, x_pos);
|
||||||
|
line += opt_param(", y={:#x}", 0, y_pos);
|
||||||
|
line += opt_param(", destX={:#x}", 0, dest_x);
|
||||||
|
line += opt_param(", destY={:#x}", 0, dest_y);
|
||||||
|
line += opt_param(", screenEdge={:#x}", 0, screen_edge);
|
||||||
|
line += opt_param(", destArea={:#x}", 0, dest_area);
|
||||||
|
line += opt_param(", destRoom={:#x}", 0, dest_room);
|
||||||
|
line += opt_param(", unknown={:#x}", 0, unknown_2);
|
||||||
|
line += opt_param(", unknown2={:#x}", 0, unknown_3);
|
||||||
|
line += opt_param(", unknown3={:#x}", 0, unknown_4);
|
||||||
|
line += opt_param(", unknown4={:#x}", 0, unknown_5);
|
||||||
|
line += opt_param(", padding={:#x}", 0, padding_1);
|
||||||
|
std::fputs(line.c_str(), file.get());
|
||||||
|
std::fputc('\n', file.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
#include "frameobjlists.h"
|
#include "frameobjlists.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fmt/format.h>
|
||||||
#include <iostream>
|
#include <util/file.h>
|
||||||
|
|
||||||
void FrameObjListsAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
void FrameObjListsAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
Reader reader(baserom, this->start, this->size);
|
Reader reader(baserom, start, size);
|
||||||
|
|
||||||
std::vector<int> first_level;
|
std::vector<u32> first_level;
|
||||||
std::vector<int> second_level;
|
std::vector<u32> second_level;
|
||||||
|
|
||||||
std::vector<std::string> lines;
|
auto file = util::open_file(assetPath, "w");
|
||||||
lines.push_back("@ First level of offsets\n");
|
std::fputs("@ First level of offsets\n", file.get());
|
||||||
|
|
||||||
while (reader.cursor < this->size) {
|
while (reader.cursor < size) {
|
||||||
if (std::find(first_level.begin(), first_level.end(), reader.cursor) != first_level.end()) {
|
if (std::find(first_level.begin(), first_level.end(), reader.cursor) != first_level.end()) {
|
||||||
// End of first level
|
// End of first level
|
||||||
break;
|
break;
|
||||||
|
@ -21,60 +21,55 @@ void FrameObjListsAsset::convertToHumanReadable(const std::vector<char>& baserom
|
||||||
|
|
||||||
u32 pointer = reader.read_u32();
|
u32 pointer = reader.read_u32();
|
||||||
first_level.push_back(pointer);
|
first_level.push_back(pointer);
|
||||||
lines.push_back(string_format("\t.4byte 0x%x\n", pointer));
|
fmt::print(file.get(), "\t.4byte {:#x}\n", pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push_back("\n@ Second level of offsets\n");
|
std::fputs("\n@ Second level of offsets\n", file.get());
|
||||||
|
|
||||||
while (reader.cursor < this->size) {
|
while (reader.cursor < size) {
|
||||||
if (std::find(second_level.begin(), second_level.end(), reader.cursor) != second_level.end()) {
|
if (std::find(second_level.begin(), second_level.end(), reader.cursor) != second_level.end()) {
|
||||||
// End of second level
|
// End of second level
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
u32 pointer = reader.read_u32();
|
u32 pointer = reader.read_u32();
|
||||||
second_level.push_back(pointer);
|
second_level.push_back(pointer);
|
||||||
lines.push_back(string_format("\t.4byte 0x%x\n", pointer));
|
fmt::print(file.get(), "\t.4byte {:#x}\n", pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int max_second_level = *std::max_element(second_level.begin(), second_level.end());
|
u32 max_second_level = *std::max_element(second_level.begin(), second_level.end());
|
||||||
|
|
||||||
while (reader.cursor < this->size) {
|
while (reader.cursor < size) {
|
||||||
if (reader.cursor > max_second_level) {
|
if (static_cast<u32>(reader.cursor) > max_second_level) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (std::find(second_level.begin(), second_level.end(), reader.cursor) == second_level.end()) {
|
if (std::find(second_level.begin(), second_level.end(), reader.cursor) == second_level.end()) {
|
||||||
// Find nearest next value that is in the second level
|
// Find nearest next value that is in the second level
|
||||||
int next = -1;
|
int next = -1;
|
||||||
for (const auto& i : second_level) {
|
for (const auto& i : second_level) {
|
||||||
if (i > reader.cursor && (next == -1 || i < next)) {
|
if (i > static_cast<u32>(reader.cursor) && (next == -1 || i < static_cast<u32>(next))) {
|
||||||
next = i;
|
next = static_cast<int>(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int diff = next - reader.cursor;
|
int diff = next - reader.cursor;
|
||||||
lines.push_back(string_format("@ Skipping %d bytes\n", diff));
|
fmt::print(file.get(), "@ Skipping {} bytes\n", diff);
|
||||||
for (int i = 0; i < diff; i++) {
|
for (int i = 0; i < diff; i++) {
|
||||||
u8 byte = reader.read_u8();
|
u8 byte = reader.read_u8();
|
||||||
lines.push_back(string_format("\t.byte %d\n", byte));
|
fmt::print(file.get(), "\t.byte {}\n", byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u8 num_objects = reader.read_u8();
|
u8 num_objects = reader.read_u8();
|
||||||
lines.push_back(string_format("\t.byte %d @ num_objs\n", num_objects));
|
fmt::print(file.get(), "\t.byte {} @ num_objs\n", num_objects);
|
||||||
|
|
||||||
for (int i = 0; i < num_objects; i++) {
|
for (int i = 0; i < num_objects; i++) {
|
||||||
s8 x_offset = reader.read_s8();
|
s8 x_offset = reader.read_s8();
|
||||||
s8 y_offset = reader.read_s8();
|
s8 y_offset = reader.read_s8();
|
||||||
u8 bitfield = reader.read_u8();
|
u8 bitfield = reader.read_u8();
|
||||||
u16 bitfield2 = reader.read_u16();
|
u16 bitfield2 = reader.read_u16();
|
||||||
lines.push_back(string_format("\tobj x=%d, y=%d", x_offset, y_offset));
|
auto line = fmt::format("\tobj x={}, y={}", x_offset, y_offset);
|
||||||
lines.push_back(opt_param(", bitfield=0x%x", 0, bitfield));
|
line += opt_param(", bitfield={:#x}", 0, bitfield);
|
||||||
lines.push_back(opt_param(", bitfield2=0x%x", 0, bitfield2));
|
line += opt_param(", bitfield2={:#x}", 0, bitfield2);
|
||||||
lines.push_back("\n");
|
std::fputs(line.c_str(), file.get());
|
||||||
|
std::fputc('\n', file.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream out(this->assetPath);
|
|
||||||
for (const auto& line : lines) {
|
|
||||||
out << line;
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
#include "gfx.h"
|
#include "gfx.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
std::filesystem::path GfxAsset::generateAssetPath() {
|
std::filesystem::path GfxAsset::generateAssetPath() {
|
||||||
std::filesystem::path pngPath = this->path;
|
std::filesystem::path pngPath = path;
|
||||||
if (pngPath.extension() == ".lz") {
|
if (pngPath.extension() == ".lz") {
|
||||||
pngPath.replace_extension("");
|
pngPath.replace_extension("");
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,11 @@ void GfxAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
cmd.push_back(toolsPath / "bin" / "gbagfx");
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
if (this->asset.contains("options")) {
|
if (asset.contains("options")) {
|
||||||
for (const auto& it : this->asset["options"].items()) {
|
for (const auto& it : asset["options"].items()) {
|
||||||
cmd.push_back("-" + it.key());
|
cmd.push_back("-" + it.key());
|
||||||
cmd.push_back(to_string(it.value()));
|
cmd.push_back(to_string(it.value()));
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ void GfxAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
void GfxAsset::buildToBinary() {
|
void GfxAsset::buildToBinary() {
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
cmd.push_back(toolsPath / "bin" / "gbagfx");
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "macroasm.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <util/file.h>
|
||||||
|
|
||||||
|
std::filesystem::path BaseMacroAsmAsset::generateAssetPath() {
|
||||||
|
std::filesystem::path asset_path = path;
|
||||||
|
return asset_path.replace_extension(".s");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path BaseMacroAsmAsset::generateBuildPath() {
|
||||||
|
std::filesystem::path build_path = path;
|
||||||
|
return build_path.replace_extension(".s");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseMacroAsmAsset::extractBinary(const std::vector<char>& baserom) {
|
||||||
|
BaseAsset::extractBinary(baserom);
|
||||||
|
// Create dummy .s file that just incbins the .bin file.
|
||||||
|
auto file = util::open_file(assetPath, "w");
|
||||||
|
fmt::print(file.get(), "\t.incbin \"{}\"\n", path.native());
|
||||||
|
}
|
|
@ -2,92 +2,83 @@
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <nlohmann/json.hpp>
|
||||||
#include <fstream>
|
#include <fmt/format.h>
|
||||||
#include <json.hpp>
|
#include <util/file.h>
|
||||||
|
|
||||||
extern std::string gBaseromPath;
|
extern std::string gBaseromPath;
|
||||||
|
|
||||||
std::filesystem::path MidiAsset::generateAssetPath() {
|
std::filesystem::path MidiAsset::generateAssetPath() {
|
||||||
std::filesystem::path path = this->path;
|
std::filesystem::path asset_path = path;
|
||||||
return path.replace_extension(".mid");
|
return asset_path.replace_extension(".mid");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path MidiAsset::generateBuildPath() {
|
std::filesystem::path MidiAsset::generateBuildPath() {
|
||||||
std::filesystem::path path = this->path;
|
std::filesystem::path build_path = path;
|
||||||
return path.replace_extension(".s");
|
return build_path.replace_extension(".s");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiAsset::extractBinary(const std::vector<char>& baserom) {
|
void MidiAsset::extractBinary(const std::vector<char>& baserom) {
|
||||||
// Custom extraction as we need a label in the middle.
|
// Custom extraction as we need a label in the middle.
|
||||||
std::string label = this->path.stem();
|
std::string label = path.stem();
|
||||||
|
|
||||||
std::filesystem::path tracksPath = this->path;
|
std::filesystem::path tracksPath = path;
|
||||||
tracksPath.replace_filename(label + "_tracks.bin");
|
tracksPath.replace_filename(label + "_tracks.bin");
|
||||||
std::filesystem::path headerPath = this->path;
|
std::filesystem::path headerPath = path;
|
||||||
headerPath.replace_filename(label + "_header.bin");
|
headerPath.replace_filename(label + "_header.bin");
|
||||||
|
|
||||||
int headerOffset = this->asset["options"]["headerOffset"];
|
int headerOffset = asset["options"]["headerOffset"];
|
||||||
|
|
||||||
// Extract tracks
|
// Extract tracks
|
||||||
{
|
{
|
||||||
auto first = baserom.begin() + this->start;
|
auto file = util::open_file(tracksPath, "w");
|
||||||
auto last = baserom.begin() + this->start + headerOffset;
|
std::fwrite(baserom.data() + start, 1, static_cast<size_t>(headerOffset), file.get());
|
||||||
std::vector<char> data(first, last);
|
|
||||||
std::fstream file(tracksPath, std::ios::out | std::ios::binary);
|
|
||||||
file.write(&data[0], data.size());
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
// Extract header
|
// Extract header
|
||||||
{
|
{
|
||||||
auto first = baserom.begin() + this->start + headerOffset;
|
auto file = util::open_file(headerPath, "w");
|
||||||
auto last = baserom.begin() + this->start + this->size;
|
std::fwrite(baserom.data() + start + headerOffset, 1, static_cast<size_t>(size - headerOffset), file.get());
|
||||||
std::vector<char> data(first, last);
|
|
||||||
std::fstream file(headerPath, std::ios::out | std::ios::binary);
|
|
||||||
file.write(&data[0], data.size());
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create dummy .s file.
|
// Create dummy .s file.
|
||||||
std::ofstream out(this->buildPath);
|
auto file = util::open_file(buildPath, "w");
|
||||||
out << "\t.incbin " << tracksPath << "\n";
|
fmt::print(file.get(), "\t.incbin \"{}\"\n", tracksPath.native());
|
||||||
out << label << "::\n";
|
fmt::print(file.get(), "{}::\n", label);
|
||||||
out << "\t.incbin " << headerPath << "\n";
|
fmt::print(file.get(), "\t.incbin \"{}\"\n", headerPath.native());
|
||||||
out.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MidiAsset::parseOptions(std::vector<std::string>& commonParams, std::vector<std::string>& agb2midParams) {
|
void MidiAsset::parseOptions(std::vector<std::string>& commonParams, std::vector<std::string>& agb2midParams) {
|
||||||
bool exactGateTime = true;
|
bool exactGateTime = true;
|
||||||
|
|
||||||
for (const auto& it : this->asset["options"].items()) {
|
for (const auto& it : asset["options"].items()) {
|
||||||
const std::string& key = it.key();
|
const std::string& key = it.key();
|
||||||
if (key == "group" || key == "G") {
|
if (key == "group" || key == "G") {
|
||||||
commonParams.push_back("-G");
|
commonParams.emplace_back("-G");
|
||||||
commonParams.push_back(to_string(it.value()));
|
commonParams.push_back(to_string(it.value()));
|
||||||
} else if (key == "priority" || key == "P") {
|
} else if (key == "priority" || key == "P") {
|
||||||
commonParams.push_back("-P");
|
commonParams.emplace_back("-P");
|
||||||
commonParams.push_back(to_string(it.value()));
|
commonParams.push_back(to_string(it.value()));
|
||||||
} else if (key == "reverb" || key == "R") {
|
} else if (key == "reverb" || key == "R") {
|
||||||
commonParams.push_back("-R");
|
commonParams.emplace_back("-R");
|
||||||
commonParams.push_back(to_string(it.value()));
|
commonParams.push_back(to_string(it.value()));
|
||||||
} else if (key == "nominator") {
|
} else if (key == "nominator") {
|
||||||
agb2midParams.push_back("-n");
|
agb2midParams.emplace_back("-n");
|
||||||
agb2midParams.push_back(to_string(it.value()));
|
agb2midParams.push_back(to_string(it.value()));
|
||||||
} else if (key == "denominator") {
|
} else if (key == "denominator") {
|
||||||
agb2midParams.push_back("-d");
|
agb2midParams.emplace_back("-d");
|
||||||
agb2midParams.push_back(to_string(it.value()));
|
agb2midParams.push_back(to_string(it.value()));
|
||||||
} else if (key == "timeChanges") {
|
} else if (key == "timeChanges") {
|
||||||
const nlohmann::json& value = it.value();
|
const nlohmann::json& value = it.value();
|
||||||
if (value.is_array()) {
|
if (value.is_array()) {
|
||||||
// Multiple time changes
|
// Multiple time changes
|
||||||
for (const auto& change : value) {
|
for (const auto& change : value) {
|
||||||
agb2midParams.push_back("-t");
|
agb2midParams.emplace_back("-t");
|
||||||
agb2midParams.push_back(to_string(change["nominator"]));
|
agb2midParams.push_back(to_string(change["nominator"]));
|
||||||
agb2midParams.push_back(to_string(change["denominator"]));
|
agb2midParams.push_back(to_string(change["denominator"]));
|
||||||
agb2midParams.push_back(to_string(change["time"]));
|
agb2midParams.push_back(to_string(change["time"]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
agb2midParams.push_back("-t");
|
agb2midParams.emplace_back("-t");
|
||||||
agb2midParams.push_back(to_string(value["nominator"]));
|
agb2midParams.push_back(to_string(value["nominator"]));
|
||||||
agb2midParams.push_back(to_string(value["denominator"]));
|
agb2midParams.push_back(to_string(value["denominator"]));
|
||||||
agb2midParams.push_back(to_string(value["time"]));
|
agb2midParams.push_back(to_string(value["time"]));
|
||||||
|
@ -107,7 +98,7 @@ void MidiAsset::parseOptions(std::vector<std::string>& commonParams, std::vector
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exactGateTime) {
|
if (exactGateTime) {
|
||||||
commonParams.push_back("-E");
|
commonParams.emplace_back("-E");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,26 +108,26 @@ void MidiAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
// Convert the options
|
// Convert the options
|
||||||
std::vector<std::string> commonParams;
|
std::vector<std::string> commonParams;
|
||||||
std::vector<std::string> agb2midParams;
|
std::vector<std::string> agb2midParams;
|
||||||
this->parseOptions(commonParams, agb2midParams);
|
parseOptions(commonParams, agb2midParams);
|
||||||
|
|
||||||
int headerOffset = this->asset["options"]["headerOffset"];
|
int headerOffset = asset["options"]["headerOffset"];
|
||||||
|
|
||||||
std::filesystem::path toolPath = "tools";
|
std::filesystem::path toolPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolPath / "agb2mid" / "agb2mid");
|
cmd.push_back(toolPath / "bin" / "agb2mid");
|
||||||
cmd.push_back(gBaseromPath);
|
cmd.push_back(gBaseromPath);
|
||||||
cmd.push_back(string_format("0x%x", this->start + headerOffset));
|
cmd.push_back(fmt::format("{:#x}", start + headerOffset));
|
||||||
cmd.push_back(gBaseromPath); // TODO deduplicate?
|
cmd.push_back(gBaseromPath); // TODO deduplicate?
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
|
cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
|
||||||
cmd.insert(cmd.end(), agb2midParams.begin(), agb2midParams.end());
|
cmd.insert(cmd.end(), agb2midParams.begin(), agb2midParams.end());
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
|
|
||||||
// We also need to build the mid to an s file here, so we get shiftability after converting.
|
// We also need to build the mid to an s file here, so we get shiftability after converting.
|
||||||
cmd.clear();
|
cmd.clear();
|
||||||
cmd.push_back(toolPath / "mid2agb" / "mid2agb");
|
cmd.push_back(toolPath / "bin" / "mid2agb");
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back(this->buildPath);
|
cmd.push_back(buildPath);
|
||||||
cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
|
cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
||||||
|
@ -145,12 +136,12 @@ void MidiAsset::buildToBinary() {
|
||||||
// Convert the options
|
// Convert the options
|
||||||
std::vector<std::string> commonParams;
|
std::vector<std::string> commonParams;
|
||||||
std::vector<std::string> agb2midParams;
|
std::vector<std::string> agb2midParams;
|
||||||
this->parseOptions(commonParams, agb2midParams);
|
parseOptions(commonParams, agb2midParams);
|
||||||
std::filesystem::path toolPath = "tools";
|
std::filesystem::path toolPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolPath / "mid2agb" / "mid2agb");
|
cmd.push_back(toolPath / "bin" / "mid2agb");
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back(this->buildPath);
|
cmd.push_back(buildPath);
|
||||||
cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
|
cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
std::filesystem::path PaletteAsset::generateAssetPath() {
|
std::filesystem::path PaletteAsset::generateAssetPath() {
|
||||||
std::filesystem::path path = this->path;
|
std::filesystem::path asset_path = path;
|
||||||
return path.replace_extension(".pal");
|
return asset_path.replace_extension(".pal");
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaletteAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
void PaletteAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
@ -11,17 +11,17 @@ void PaletteAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
cmd.push_back(toolsPath / "bin" / "gbagfx");
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaletteAsset::buildToBinary() {
|
void PaletteAsset::buildToBinary() {
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
cmd.push_back(toolsPath / "bin" / "gbagfx");
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "spriteframe.h"
|
||||||
|
#include "reader.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <util/file.h>
|
||||||
|
|
||||||
|
void SpriteFrameAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
Reader reader(baserom, start, size);
|
||||||
|
auto file = util::open_file(assetPath, "w");
|
||||||
|
while (reader.cursor < size) {
|
||||||
|
u8 num_gfx_tiles = reader.read_u8();
|
||||||
|
u8 unk = reader.read_u8();
|
||||||
|
u16 first_gfx_tile_index = reader.read_u16();
|
||||||
|
|
||||||
|
auto line = fmt::format("\tsprite_frame first_tile_index={:#x}", first_gfx_tile_index);
|
||||||
|
line += opt_param(", num_tiles={}", 0, num_gfx_tiles);
|
||||||
|
line += opt_param(", unknown={:#x}", 0, unk);
|
||||||
|
std::fputs(line.c_str(), file.get());
|
||||||
|
std::fputc('\n', file.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
std::filesystem::path TilesetAsset::generateAssetPath() {
|
std::filesystem::path TilesetAsset::generateAssetPath() {
|
||||||
std::filesystem::path pngPath = this->path;
|
std::filesystem::path pngPath = path;
|
||||||
if (pngPath.extension() == ".lz") {
|
if (pngPath.extension() == ".lz") {
|
||||||
pngPath.replace_extension("");
|
pngPath.replace_extension("");
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ void TilesetAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
|
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
cmd.push_back(toolsPath / "bin" / "gbagfx");
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back("-mwidth");
|
cmd.push_back("-mwidth");
|
||||||
cmd.push_back("32");
|
cmd.push_back("32");
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
|
@ -26,8 +26,8 @@ void TilesetAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||||
void TilesetAsset::buildToBinary() {
|
void TilesetAsset::buildToBinary() {
|
||||||
std::filesystem::path toolsPath = "tools";
|
std::filesystem::path toolsPath = "tools";
|
||||||
std::vector<std::string> cmd;
|
std::vector<std::string> cmd;
|
||||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
cmd.push_back(toolsPath / "bin" / "gbagfx");
|
||||||
cmd.push_back(this->assetPath);
|
cmd.push_back(assetPath);
|
||||||
cmd.push_back(this->path);
|
cmd.push_back(path);
|
||||||
check_call(cmd);
|
check_call(cmd);
|
||||||
}
|
}
|
|
@ -12,7 +12,8 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
@ -107,12 +108,12 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
// Read baserom.
|
// Read baserom.
|
||||||
std::ifstream file(gBaseromPath, std::ios::binary | std::ios::ate);
|
std::ifstream file(gBaseromPath, std::ios::binary | std::ios::ate);
|
||||||
std::streamsize size = file.tellg();
|
auto size = file.tellg();
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
std::vector<char> baserom(size);
|
std::vector<char> baserom(static_cast<size_t>(size));
|
||||||
if (!file.read(baserom.data(), size)) {
|
if (!file.read(baserom.data(), size)) {
|
||||||
std::cerr << "Could not read baserom " << gBaseromPath << std::endl;
|
fmt::print(stderr, "Could not read baserom {}\n", gBaseromPath);
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
|
@ -141,17 +142,23 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
std::unique_ptr<OffsetCalculator> offsetCalculator;
|
std::unique_ptr<OffsetCalculator> offsetCalculator;
|
||||||
|
|
||||||
uint currentOffset = 0;
|
int currentOffset = 0;
|
||||||
for (const auto& asset : assets) {
|
for (const auto& asset : assets) {
|
||||||
if (asset.contains("offsets")) { // Offset definition
|
if (asset.contains("offsets")) { // Offset definition
|
||||||
if (asset["offsets"].contains(gVariant)) {
|
if (asset["offsets"].contains(gVariant)) {
|
||||||
currentOffset = asset["offsets"][gVariant];
|
currentOffset = asset["offsets"][gVariant];
|
||||||
}
|
}
|
||||||
} else if (asset.contains("calculateOffsets")) { // Start offset calculation
|
} else if (asset.contains("calculateOffsets")) { // Start offset calculation
|
||||||
std::filesystem::path path = gAssetsFolder;
|
if (gMode == EXTRACT || gMode == BUILD) {
|
||||||
path = path / asset["calculateOffsets"];
|
std::filesystem::path path = gAssetsFolder;
|
||||||
int baseOffset = asset["start"].get<int>() + currentOffset;
|
path = path / asset["calculateOffsets"];
|
||||||
offsetCalculator = std::make_unique<OffsetCalculator>(path, baseOffset);
|
int baseOffset = 0;
|
||||||
|
// During build mode the offsets are calculated directly instead of from a base address.
|
||||||
|
if (gMode == EXTRACT) {
|
||||||
|
baseOffset = asset["start"].get<int>() + currentOffset;
|
||||||
|
}
|
||||||
|
offsetCalculator = std::make_unique<OffsetCalculator>(path, baseOffset);
|
||||||
|
}
|
||||||
} else if (asset.contains("path")) { // Asset definition
|
} else if (asset.contains("path")) { // Asset definition
|
||||||
|
|
||||||
if (asset.contains("variants")) {
|
if (asset.contains("variants")) {
|
||||||
|
@ -181,6 +188,11 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
case CONVERT: {
|
case CONVERT: {
|
||||||
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
||||||
|
if (!std::filesystem::exists(assetHandler->getBuildPath())) {
|
||||||
|
std::cerr << "Error: Extracted binary file " << assetHandler->getBuildPath()
|
||||||
|
<< " does not exist. Run `make` first." << std::endl;
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
if (shouldConvertAsset(assetHandler)) {
|
if (shouldConvertAsset(assetHandler)) {
|
||||||
if (gVerbose) {
|
if (gVerbose) {
|
||||||
std::cout << "Converting " << assetHandler->getAssetPath() << "..." << std::endl;
|
std::cout << "Converting " << assetHandler->getAssetPath() << "..." << std::endl;
|
||||||
|
@ -191,12 +203,29 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
case BUILD: {
|
case BUILD: {
|
||||||
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
||||||
|
if (!std::filesystem::exists(assetHandler->getAssetPath())) {
|
||||||
|
std::cerr << "Error: Extracted asset file " << assetHandler->getAssetPath()
|
||||||
|
<< " does not exist. Run `make extractassets` first." << std::endl;
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
if (shouldBuildAsset(assetHandler)) {
|
if (shouldBuildAsset(assetHandler)) {
|
||||||
if (gVerbose) {
|
if (gVerbose) {
|
||||||
std::cout << "Building " << assetHandler->getAssetPath() << "..." << std::endl;
|
std::cout << "Building " << assetHandler->getAssetPath() << "..." << std::endl;
|
||||||
}
|
}
|
||||||
buildAsset(assetHandler);
|
buildAsset(assetHandler);
|
||||||
}
|
}
|
||||||
|
if (offsetCalculator != nullptr) {
|
||||||
|
// New start is the end of the previous asset.
|
||||||
|
int start = offsetCalculator->getLastEnd();
|
||||||
|
// Get the size of the current asset and calculate the end position.
|
||||||
|
int filesize = static_cast<int>(std::filesystem::file_size(assetHandler->getBuildPath()));
|
||||||
|
// Align by four bytes.
|
||||||
|
if (filesize % 4 != 0) {
|
||||||
|
filesize += 4 - (filesize % 4);
|
||||||
|
}
|
||||||
|
offsetCalculator->setLastEnd(start + filesize);
|
||||||
|
offsetCalculator->addAsset(start, assetHandler->getSymbol());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +250,7 @@ std::unique_ptr<BaseAsset> getAssetHandlerByType(const std::filesystem::path& pa
|
||||||
start = asset["starts"][gVariant];
|
start = asset["starts"][gVariant];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string type = "";
|
std::string type;
|
||||||
if (asset.contains("type")) {
|
if (asset.contains("type")) {
|
||||||
type = asset["type"];
|
type = asset["type"];
|
||||||
}
|
}
|
||||||
|
@ -256,11 +285,11 @@ std::unique_ptr<BaseAsset> getAssetHandlerByType(const std::filesystem::path& pa
|
||||||
type == "map_collision" || type == "unknown") {
|
type == "map_collision" || type == "unknown") {
|
||||||
// TODO implement conversions
|
// TODO implement conversions
|
||||||
assetHandler = std::make_unique<BaseAsset>(path, start, size, asset);
|
assetHandler = std::make_unique<BaseAsset>(path, start, size, asset);
|
||||||
} else if (type == "") {
|
} else if (type.empty()) {
|
||||||
// Unknown binary asset
|
// Unknown binary asset
|
||||||
assetHandler = std::make_unique<BaseAsset>(path, start, size, asset);
|
assetHandler = std::make_unique<BaseAsset>(path, start, size, asset);
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Error: Unimplemented asset type `" << type << "`" << std::endl;
|
fmt::print(stderr, "Error: Unimplemented asset type \"{}\"", type);
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
assetHandler->setup();
|
assetHandler->setup();
|
||||||
|
@ -287,7 +316,7 @@ bool shouldExtractAsset(const std::filesystem::path& path, const std::filesystem
|
||||||
|
|
||||||
void extractAsset(std::unique_ptr<BaseAsset>& assetHandler, const std::vector<char>& baserom) {
|
void extractAsset(std::unique_ptr<BaseAsset>& assetHandler, const std::vector<char>& baserom) {
|
||||||
// Create the parent directory
|
// Create the parent directory
|
||||||
std::filesystem::path parentDir = std::filesystem::path(assetHandler->getPath());
|
std::filesystem::path parentDir = assetHandler->getPath();
|
||||||
parentDir.remove_filename();
|
parentDir.remove_filename();
|
||||||
std::filesystem::create_directories(parentDir);
|
std::filesystem::create_directories(parentDir);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "offsets.h"
|
||||||
|
|
||||||
|
OffsetCalculator::OffsetCalculator(const std::filesystem::path& outputFile, int baseOffset_)
|
||||||
|
: output(outputFile), baseOffset(baseOffset_), lastEnd(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffsetCalculator::addAsset(int start, const std::string& symbol) {
|
||||||
|
output << "\t.equiv offset_" << symbol << ", " << start - baseOffset << std::endl;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef OFFSETS_H
|
||||||
|
#define OFFSETS_H
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class OffsetCalculator {
|
||||||
|
|
||||||
|
public:
|
||||||
|
OffsetCalculator(const std::filesystem::path& offsetsFile, int baseOffset_);
|
||||||
|
void addAsset(int start, const std::string& symbol);
|
||||||
|
[[nodiscard]] int getLastEnd() const {
|
||||||
|
return lastEnd;
|
||||||
|
}
|
||||||
|
void setLastEnd(int lastEnd_) {
|
||||||
|
this->lastEnd = lastEnd_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::ofstream output;
|
||||||
|
int baseOffset;
|
||||||
|
// Store the end of the previously added asset
|
||||||
|
int lastEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef READER_H
|
||||||
|
#define READER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <util/types.h>
|
||||||
|
|
||||||
|
class Reader {
|
||||||
|
public:
|
||||||
|
Reader(const std::vector<char>& baserom, int start, [[maybe_unused]]int size_)
|
||||||
|
: data(baserom.data() + start) //, size(size_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] s8 read_s8() {
|
||||||
|
// TODO range check
|
||||||
|
return data[static_cast<unsigned long>(cursor++)];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u8 read_u8() {
|
||||||
|
return static_cast<u8>(read_s8());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u16 read_u16() {
|
||||||
|
return static_cast<u16>(read_u8() + (read_u8() << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u32 read_u32() {
|
||||||
|
return static_cast<u32>(read_u16() + (read_u16() << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
int cursor = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* data;
|
||||||
|
// const int size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,6 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
void check_call(const std::vector<std::string>& cmd) {
|
void check_call(const std::vector<std::string>& cmd) {
|
||||||
std::string cmdstr;
|
std::string cmdstr;
|
||||||
|
@ -18,3 +19,10 @@ void check_call(const std::vector<std::string>& cmd) {
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string opt_param(const std::string& format, int defaultVal, int value) {
|
||||||
|
if (value != defaultVal) {
|
||||||
|
return fmt::format(format, value);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void check_call(const std::vector<std::string>& cmd);
|
||||||
|
|
||||||
|
std::string opt_param(const std::string& format, int defaultVal, int value);
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue