diff options
-rw-r--r-- | Makefile | 117 | ||||
-rw-r--r-- | README.md | 31 | ||||
-rw-r--r-- | rom.sha1 | 1 | ||||
-rw-r--r-- | src/audio/music1.asm | 116 | ||||
-rw-r--r-- | src/audio/music2.asm | 116 | ||||
-rw-r--r-- | src/audio/sfx.asm | 18 | ||||
-rw-r--r-- | src/engine/bank04.asm | 4 | ||||
-rw-r--r-- | src/engine/bank1c.asm | 6 | ||||
-rw-r--r-- | src/engine/home.asm | 244 | ||||
-rw-r--r-- | src/gfx/cards/grassenergy.8x6.png | bin | 542 -> 593 bytes | |||
-rw-r--r-- | src/gfx/duel/cgb_symbols.t7.png | bin | 2960 -> 1270 bytes | |||
-rw-r--r-- | src/gfx/duel/dmg_sgb_symbols.t7.png | bin | 3107 -> 1374 bytes | |||
-rw-r--r-- | src/gfx/duel/other.t7.png | bin | 1588 -> 820 bytes | |||
-rw-r--r-- | src/gfx/fonts/full_width/4.t3.png | bin | 2279 -> 1153 bytes | |||
-rw-r--r-- | src/gfx/fonts/symbols.png | bin | 1398 -> 726 bytes | |||
-rw-r--r-- | src/layout.link (renamed from tcg.link) | 0 | ||||
-rw-r--r-- | src/rgbdscheck.asm | 12 | ||||
-rw-r--r-- | tools/.gitignore | 2 | ||||
-rw-r--r-- | tools/Makefile | 16 | ||||
-rw-r--r-- | tools/common.h | 40 | ||||
-rw-r--r-- | tools/gfx.c | 345 | ||||
-rw-r--r-- | tools/scan_includes.c | 197 |
22 files changed, 974 insertions, 291 deletions
@@ -1,39 +1,106 @@ -.PHONY: all compare clean +rom := poketcg.gbc + +rom_obj := \ +src/main.o \ +src/gfx.o \ +src/text.o \ +src/audio.o \ +src/wram.o \ +src/hram.o + + +### Build tools + +ifeq (,$(shell which sha1sum)) +SHA1 := shasum +else +SHA1 := sha1sum +endif + +RGBDS ?= +RGBASM ?= $(RGBDS)rgbasm +RGBFIX ?= $(RGBDS)rgbfix +RGBGFX ?= $(RGBDS)rgbgfx +RGBLINK ?= $(RGBDS)rgblink + + +### Build targets .SUFFIXES: -.SUFFIXES: .asm .o .gbc .png .2bpp .1bpp .pal .SECONDEXPANSION: +.PRECIOUS: +.SECONDARY: +.PHONY: all tcg clean tidy compare tools + +all: $(rom) +tcg: $(rom) + +clean: tidy + find src/gfx \( -iname '*.1bpp' -o -iname '*.2bpp' \) -delete + +tidy: + rm -f $(rom) $(rom_obj) $(rom:.gbc=.map) $(rom:.gbc=.sym) src/rgbdscheck.o + $(MAKE) clean -C tools/ + +compare: $(rom) + @$(SHA1) -c rom.sha1 + +tools: + $(MAKE) -C tools/ + + +RGBASMFLAGS = -h -i src/ -L -Weverything +# Create a sym/map for debug purposes if `make` run with `DEBUG=1` +ifeq ($(DEBUG),1) +RGBASMFLAGS += -E +endif + +src/rgbdscheck.o: src/rgbdscheck.asm + $(RGBASM) -o $@ $< + +# The dep rules have to be explicit or else missing files won't be reported. +# As a side effect, they're evaluated immediately instead of when the rule is invoked. +# It doesn't look like $(shell) can be deferred so there might not be a better way. +define DEP +$1: $2 $$(shell tools/scan_includes -s -i src/ $2) | src/rgbdscheck.o + $$(RGBASM) $$(RGBASMFLAGS) -o $$@ $$< +endef + +# Build tools when building the rom. +# This has to happen before the rules are processed, since that's when scan_includes is run. +ifeq (,$(filter clean tidy tools,$(MAKECMDGOALS))) + +$(info $(shell $(MAKE) -C tools)) + +# Dependencies for objects +$(foreach obj, $(rom_obj), $(eval $(call DEP,$(obj),$(obj:.o=.asm)))) + +endif + + +%.asm: ; + -OBJS = src/main.o src/gfx.o src/text.o src/audio.o src/wram.o src/hram.o -EXTRAS = tools +opts = -cjsv -k 01 -l 0x33 -m 0x1b -p 0 -r 03 -t POKECARD -i AXQE -$(foreach obj, $(OBJS), \ - $(eval $(obj:.o=)_dep = $(shell python $(EXTRAS)/scan_includes.py $(obj:.o=.asm))) \ -) +$(rom): $(rom_obj) src/layout.link + $(RGBLINK) -m $(rom:.gbc=.map) -n $(rom:.gbc=.sym) -l src/layout.link -o $@ $(filter %.o,$^) + $(RGBFIX) $(opts) $@ -all: tcg.gbc compare -compare: baserom.gbc tcg.gbc - cmp $^ +### Misc file-specific graphics rules -$(OBJS): $$*.asm $$($$*_dep) - @python $(EXTRAS)/gfx.py 2bpp $(2bppq) - @python $(EXTRAS)/gfx.py 1bpp $(1bppq) - rgbasm -h -i src/ -o $@ $< -tcg.gbc: $(OBJS) - rgblink -n $*.sym -m $*.map -l $*.link -o $@ $^ - rgbfix -cjsv -k 01 -l 0x33 -m 0x1b -p 0 -r 03 -t POKECARD -i AXQE $@ - sort $*.sym -o $*.sym +### Catch-all graphics rules -clean: - rm -f tcg.gbc $(OBJS) *.sym *.map - find . \( -iname '*.1bpp' -o -iname '*.2bpp' \) -exec rm {} + +%.png: ; %.2bpp: %.png - $(eval 2bppq += $<) - @rm -f $@ + $(RGBGFX) $(rgbgfx) -o $@ $< + $(if $(tools/gfx),\ + tools/gfx $(tools/gfx) -o $@ $@) %.1bpp: %.png - $(eval 1bppq += $<) - @rm -f $@ + $(RGBGFX) $(rgbgfx) -d1 -o $@ $< + $(if $(tools/gfx),\ + tools/gfx $(tools/gfx) -d1 -o $@ $@) @@ -4,27 +4,30 @@ This is a disassembly of Pokémon TCG. It uses the following ROM as a base: -* Pokémon Trading Card Game (U) [C][!].gbc `md5: 219b2cc64e5a052003015d4bd4c622cd` +- Pokémon Trading Card Game (U) [C][!].gbc `sha1: 0f8670a583255cff3e5b7ca71b5d7454d928fc48` To assemble, first download RGBDS (https://github.com/gbdev/rgbds/releases) and extract it to /usr/local/bin. Copy the above ROM to this directory as "baserom.gbc". Run `make` in your shell. -This will output a file named "tcg.gbc". +This will output a file named "poketcg.gbc". -# See Also +## See also -* Disassembly of [**Pokémon Red/Blue**][pokered] -* Disassembly of [**Pokémon Yellow**][pokeyellow] -* Disassembly of [**Pokémon Gold**][pokegold] -* Disassembly of [**Pokémon Crystal**][pokecrystal] -* Disassembly of [**Pokémon Pinball**][pokepinball] -* Disassembly of [**Pokémon Ruby**][pokeruby] -* Disassembly of [**Pokémon Fire Red**][pokefirered] -* Disassembly of [**Pokémon Emerald**][pokeemerald] -* Discord: [**pret**][Discord] -* irc: **irc.freenode.net** [**#pret**][irc] +- **Discord:** [pret][discord] +- **IRC:** [freenode#pret][irc] + +Other disassembly projects: + +- [**Pokémon Red/Blue**][pokered] +- [**Pokémon Yellow**][pokeyellow] +- [**Pokémon Gold/Silver**][pokegold] +- [**Pokémon Crystal**][pokecrystal] +- [**Pokémon Pinball**][pokepinball] +- [**Pokémon Ruby**][pokeruby] +- [**Pokémon FireRed**][pokefirered] +- [**Pokémon Emerald**][pokeemerald] [pokered]: https://github.com/pret/pokered [pokeyellow]: https://github.com/pret/pokeyellow @@ -34,5 +37,5 @@ This will output a file named "tcg.gbc". [pokeruby]: https://github.com/pret/pokeruby [pokefirered]: https://github.com/pret/pokefirered [pokeemerald]: https://github.com/pret/pokeemerald -[Discord]: https://discord.gg/d5dubZ3 +[discord]: https://discord.gg/d5dubZ3 [irc]: https://kiwiirc.com/client/irc.freenode.net/?#pret diff --git a/rom.sha1 b/rom.sha1 new file mode 100644 index 0000000..335f3e8 --- /dev/null +++ b/rom.sha1 @@ -0,0 +1 @@ +0f8670a583255cff3e5b7ca71b5d7454d928fc48 *poketcg.gbc diff --git a/src/audio/music1.asm b/src/audio/music1.asm index a3a6e9e..03c55b9 100644 --- a/src/audio/music1.asm +++ b/src/audio/music1.asm @@ -106,13 +106,13 @@ Func_f406f: ; f406f (3d:406f) Music1_Init: ; f407d (3d:407d) xor a - ld [rNR52], a + ldh [rNR52], a ld a, $80 - ld [rNR52], a + ldh [rNR52], a ld a, $77 - ld [rNR50], a + ldh [rNR50], a ld a, $ff - ld [rNR51], a + ldh [rNR51], a ld a, $3d ld [wCurSongBank], a ld a, $80 @@ -216,34 +216,34 @@ Music1_StopAllChannels: ; f414b (3d:414b) bit 0, d jr nz, .stop_channel_2 ld a, $8 - ld [rNR12], a + ldh [rNR12], a swap a - ld [rNR14], a + ldh [rNR14], a .stop_channel_2 xor a ld [wMusicIsPlaying + 1], a bit 1, d jr nz, .stop_channel_4 ld a, $8 - ld [rNR22], a + ldh [rNR22], a swap a - ld [rNR24], a + ldh [rNR24], a .stop_channel_4 xor a ld [wMusicIsPlaying + 3], a bit 3, d jr nz, .stop_channel_3 ld a, $8 - ld [rNR42], a + ldh [rNR42], a swap a - ld [rNR44], a + ldh [rNR44], a .stop_channel_3 xor a ld [wMusicIsPlaying + 2], a bit 2, d jr nz, .done ld a, $0 - ld [rNR32], a + ldh [rNR32], a .done ret @@ -429,9 +429,9 @@ Music1_UpdateChannel1: ; f42a5 (3d:42a5) bit 0, a jr nz, .asm_f4309 ld a, $8 - ld [rNR12], a + ldh [rNR12], a swap a - ld [rNR14], a + ldh [rNR14], a .asm_f4309 ret @@ -482,9 +482,9 @@ Music1_UpdateChannel2: ; f430a (3d:430a) bit 1, a jr nz, .asm_f436e ld a, $8 - ld [rNR22], a + ldh [rNR22], a swap a - ld [rNR24], a + ldh [rNR24], a .asm_f436e ret @@ -506,7 +506,7 @@ Music1_UpdateChannel3: ; f436f (3d:436f) cp $1 jr z, .asm_f4398 ld a, [wMusicE9 + 2] - ld [rNR32], a + ldh [rNR32], a .asm_f4398 ld a, [wddbb + 2] dec a @@ -531,9 +531,9 @@ Music1_UpdateChannel3: ; f436f (3d:436f) bit 2, a jr nz, .asm_f43cd ld a, $0 - ld [rNR32], a + ldh [rNR32], a ld a, $80 - ld [rNR34], a + ldh [rNR34], a .asm_f43cd ret @@ -569,9 +569,9 @@ Music1_UpdateChannel4: ; f43ce (3d:43ce) xor a ld [wddef], a ld a, $8 - ld [rNR42], a + ldh [rNR42], a swap a - ld [rNR44], a + ldh [rNR44], a .asm_f4413 ret @@ -1167,19 +1167,19 @@ Func_f4714: ; f4714 (3d:4714) cp $80 jr z, .asm_f4733 ld a, [wMusicVolume] - ld [rNR12], a + ldh [rNR12], a ld d, $80 .asm_f4733 ld [hl], $2 ld a, $8 - ld [rNR10], a + ldh [rNR10], a ld a, [wMusicDuty1] - ld [rNR11], a + ldh [rNR11], a ld a, [wMusicCh1CurPitch] - ld [rNR13], a + ldh [rNR13], a ld a, [wMusicCh1CurOctave] or d - ld [rNR14], a + ldh [rNR14], a .asm_f4749 ret .asm_f474a @@ -1206,17 +1206,17 @@ Func_f475a: ; f475a (3d:475a) cp $80 jr z, .asm_f4779 ld a, [wMusicVolume + 1] - ld [rNR22], a + ldh [rNR22], a ld d, $80 .asm_f4779 ld [hl], $2 ld a, [wMusicDuty2] - ld [rNR21], a + ldh [rNR21], a ld a, [wMusicCh2CurPitch] - ld [rNR23], a + ldh [rNR23], a ld a, [wMusicCh2CurOctave] or d - ld [rNR24], a + ldh [rNR24], a .asm_f478b ret .asm_f478c @@ -1239,7 +1239,7 @@ Func_f479c: ; f479c (3d:479c) or a jr z, .no_wave_change xor a - ld [rNR30], a + ldh [rNR30], a call Music1_LoadWaveInstrument ld d, $80 .no_wave_change @@ -1251,28 +1251,28 @@ Func_f479c: ; f479c (3d:479c) cp $80 jr z, .asm_f47cc ld a, [wMusicVolume + 2] - ld [rNR32], a + ldh [rNR32], a xor a - ld [rNR30], a + ldh [rNR30], a ld d, $80 .asm_f47cc ld [hl], $2 xor a - ld [rNR31], a + ldh [rNR31], a ld a, [wMusicCh3CurPitch] - ld [rNR33], a + ldh [rNR33], a ld a, $80 - ld [rNR30], a + ldh [rNR30], a ld a, [wMusicCh3CurOctave] or d - ld [rNR34], a + ldh [rNR34], a .asm_f47e0 ret .asm_f47e1 ld hl, wMusicTie ld [hl], $0 xor a - ld [rNR30], a + ldh [rNR30], a ret Music1_LoadWaveInstrument: ; f479c (3d:47ea) @@ -1349,7 +1349,7 @@ Func_f4839: ; f4839 (3d:4839) jr nz, .asm_f4853 jr asm_f482a .asm_f4853 - ld [rNR43], a + ldh [rNR43], a inc de ld a, d ld [hld], a @@ -1368,7 +1368,7 @@ Func_f485a: ; f485a (3d:485a) Func_f4866: ; f4866 (3d:4866) ld a, [wMusicPanning] - ld [rNR50], a + ldh [rNR50], a ld a, [wdd8c] or a ld hl, wMusicDC @@ -1397,7 +1397,7 @@ Func_f4866: ; f4866 (3d:4866) swap e or e and d - ld [rNR51], a + ldh [rNR51], a ret Music1_UpdateVibrato: ; f4898 (3d:4898) @@ -1495,13 +1495,13 @@ Func_f490b: ; f490b (3d:490b) bit 0, a jr nz, .done ld a, e - ld [rNR13], a - ld a, [rNR11] + ldh [rNR13], a + ldh a, [rNR11] and $c0 - ld [rNR11], a + ldh [rNR11], a ld a, d and $3f - ld [rNR14], a + ldh [rNR14], a ret .not_channel_1 cp $1 @@ -1513,12 +1513,12 @@ Func_f490b: ; f490b (3d:490b) bit 1, a jr nz, .done ld a, e - ld [rNR23], a - ld a, [rNR21] + ldh [rNR23], a + ldh a, [rNR21] and $c0 - ld [rNR21], a + ldh [rNR21], a ld a, d - ld [rNR24], a + ldh [rNR24], a ret .not_channel_2 cp $2 @@ -1530,11 +1530,11 @@ Func_f490b: ; f490b (3d:490b) bit 2, a jr nz, .done ld a, e - ld [rNR33], a + ldh [rNR33], a xor a - ld [rNR31], a + ldh [rNR31], a ld a, d - ld [rNR34], a + ldh [rNR34], a .done ret @@ -1567,28 +1567,28 @@ Func_f4980: ; f4980 (3d:4980) bit 0, d jr nz, .asm_f4990 ld a, $8 - ld [rNR12], a + ldh [rNR12], a swap a - ld [rNR14], a + ldh [rNR14], a .asm_f4990 bit 1, d jr nz, .asm_f499c swap a - ld [rNR22], a + ldh [rNR22], a swap a - ld [rNR24], a + ldh [rNR24], a .asm_f499c bit 3, d jr nz, .asm_f49a8 swap a - ld [rNR42], a + ldh [rNR42], a swap a - ld [rNR44], a + ldh [rNR44], a .asm_f49a8 bit 2, d jr nz, .asm_f49b0 ld a, $0 - ld [rNR32], a + ldh [rNR32], a .asm_f49b0 ret diff --git a/src/audio/music2.asm b/src/audio/music2.asm index e13389e..4a8178e 100644 --- a/src/audio/music2.asm +++ b/src/audio/music2.asm @@ -106,13 +106,13 @@ Func_f806f: ; f806f (3e:406f) Music2_Init: ; f807d (3e:407d) xor a - ld [rNR52], a + ldh [rNR52], a ld a, $80 - ld [rNR52], a + ldh [rNR52], a ld a, $77 - ld [rNR50], a + ldh [rNR50], a ld a, $ff - ld [rNR51], a + ldh [rNR51], a ld a, $3d ld [wCurSongBank], a ld a, $80 @@ -216,34 +216,34 @@ Music2_StopAllChannels: ; f814b (3e:414b) bit 0, d jr nz, .stop_channel_2 ld a, $8 - ld [rNR12], a + ldh [rNR12], a swap a - ld [rNR14], a + ldh [rNR14], a .stop_channel_2 xor a ld [wMusicIsPlaying + 1], a bit 1, d jr nz, .stop_channel_4 ld a, $8 - ld [rNR22], a + ldh [rNR22], a swap a - ld [rNR24], a + ldh [rNR24], a .stop_channel_4 xor a ld [wMusicIsPlaying + 3], a bit 3, d jr nz, .stop_channel_3 ld a, $8 - ld [rNR42], a + ldh [rNR42], a swap a - ld [rNR44], a + ldh [rNR44], a .stop_channel_3 xor a ld [wMusicIsPlaying + 2], a bit 2, d jr nz, .done ld a, $0 - ld [rNR32], a + ldh [rNR32], a .done ret @@ -429,9 +429,9 @@ Music2_UpdateChannel1: ; f82a5 (3e:42a5) bit 0, a jr nz, .asm_f8309 ld a, $8 - ld [rNR12], a + ldh [rNR12], a swap a - ld [rNR14], a + ldh [rNR14], a .asm_f8309 ret @@ -482,9 +482,9 @@ Music2_UpdateChannel2: ; f830a (3e:430a) bit 1, a jr nz, .asm_f836e ld a, $8 - ld [rNR22], a + ldh [rNR22], a swap a - ld [rNR24], a + ldh [rNR24], a .asm_f836e ret @@ -506,7 +506,7 @@ Music2_UpdateChannel3: ; f836f (3e:436f) cp $1 jr z, .asm_f8398 ld a, [wMusicE9 + 2] - ld [rNR32], a + ldh [rNR32], a .asm_f8398 ld a, [wddbb + 2] dec a @@ -531,9 +531,9 @@ Music2_UpdateChannel3: ; f836f (3e:436f) bit 2, a jr nz, .asm_f83cd ld a, $0 - ld [rNR32], a + ldh [rNR32], a ld a, $80 - ld [rNR34], a + ldh [rNR34], a .asm_f83cd ret @@ -569,9 +569,9 @@ Music2_UpdateChannel4: ; f83ce (3e:43ce) xor a ld [wddef], a ld a, $8 - ld [rNR42], a + ldh [rNR42], a swap a - ld [rNR44], a + ldh [rNR44], a .asm_f8413 ret @@ -1167,19 +1167,19 @@ Func_f8714: ; f8714 (3e:4714) cp $80 jr z, .asm_f8733 ld a, [wMusicVolume] - ld [rNR12], a + ldh [rNR12], a ld d, $80 .asm_f8733 ld [hl], $2 ld a, $8 - ld [rNR10], a + ldh [rNR10], a ld a, [wMusicDuty1] - ld [rNR11], a + ldh [rNR11], a ld a, [wMusicCh1CurPitch] - ld [rNR13], a + ldh [rNR13], a ld a, [wMusicCh1CurOctave] or d - ld [rNR14], a + ldh [rNR14], a .asm_f8749 ret .asm_f874a @@ -1206,17 +1206,17 @@ Func_f875a: ; f875a (3e:475a) cp $80 jr z, .asm_f8779 ld a, [wMusicVolume + 1] - ld [rNR22], a + ldh [rNR22], a ld d, $80 .asm_f8779 ld [hl], $2 ld a, [wMusicDuty2] - ld [rNR21], a + ldh [rNR21], a ld a, [wMusicCh2CurPitch] - ld [rNR23], a + ldh [rNR23], a ld a, [wMusicCh2CurOctave] or d - ld [rNR24], a + ldh [rNR24], a .asm_f878b ret .asm_f878c @@ -1239,7 +1239,7 @@ Func_f879c: ; f879c (3e:479c) or a jr z, .no_wave_change xor a - ld [rNR30], a + ldh [rNR30], a call Music2_LoadWaveInstrument ld d, $80 .no_wave_change @@ -1251,28 +1251,28 @@ Func_f879c: ; f879c (3e:479c) cp $80 jr z, .asm_f87cc ld a, [wMusicVolume + 2] - ld [rNR32], a + ldh [rNR32], a xor a - ld [rNR30], a + ldh [rNR30], a ld d, $80 .asm_f87cc ld [hl], $2 xor a - ld [rNR31], a + ldh [rNR31], a ld a, [wMusicCh3CurPitch] - ld [rNR33], a + ldh [rNR33], a ld a, $80 - ld [rNR30], a + ldh [rNR30], a ld a, [wMusicCh3CurOctave] or d - ld [rNR34], a + ldh [rNR34], a .asm_f87e0 ret .asm_f87e1 ld hl, wMusicTie ld [hl], $0 xor a - ld [rNR30], a + ldh [rNR30], a ret Music2_LoadWaveInstrument: ; f879c (3e:47ea) @@ -1349,7 +1349,7 @@ Func_f8839: ; f8839 (3e:4839) jr nz, .asm_f8853 jr asm_f882a .asm_f8853 - ld [rNR43], a + ldh [rNR43], a inc de ld a, d ld [hld], a @@ -1368,7 +1368,7 @@ Func_f885a: ; f885a (3e:485a) Func_f8866: ; f8866 (3e:4866) ld a, [wMusicPanning] - ld [rNR50], a + ldh [rNR50], a ld a, [wdd8c] or a ld hl, wMusicDC @@ -1397,7 +1397,7 @@ Func_f8866: ; f8866 (3e:4866) swap e or e and d - ld [rNR51], a + ldh [rNR51], a ret Func_f8898: ; f8898 (3e:4898) @@ -1495,13 +1495,13 @@ Func_f890b: ; f890b (3e:490b) bit 0, a jr nz, .done ld a, e - ld [rNR13], a - ld a, [rNR11] + ldh [rNR13], a + ldh a, [rNR11] and $c0 - ld [rNR11], a + ldh [rNR11], a ld a, d and $3f - ld [rNR14], a + ldh [rNR14], a ret .not_channel_1 cp $1 @@ -1513,12 +1513,12 @@ Func_f890b: ; f890b (3e:490b) bit 1, a jr nz, .done ld a, e - ld [rNR23], a - ld a, [rNR21] + ldh [rNR23], a + ldh a, [rNR21] and $c0 - ld [rNR21], a + ldh [rNR21], a ld a, d - ld [rNR24], a + ldh [rNR24], a ret .not_channel_2 cp $2 @@ -1530,11 +1530,11 @@ Func_f890b: ; f890b (3e:490b) bit 2, a jr nz, .done ld a, e - ld [rNR33], a + ldh [rNR33], a xor a - ld [rNR31], a + ldh [rNR31], a ld a, d - ld [rNR34], a + ldh [rNR34], a .done ret @@ -1567,28 +1567,28 @@ Func_f8980: ; f8980 (3e:4980) bit 0, d jr nz, .asm_f8990 ld a, $8 - ld [rNR12], a + ldh [rNR12], a swap a - ld [rNR14], a + ldh [rNR14], a .asm_f8990 bit 1, d jr nz, .asm_f899c swap a - ld [rNR22], a + ldh [rNR22], a swap a - ld [rNR24], a + ldh [rNR24], a .asm_f899c bit 3, d jr nz, .asm_f89a8 swap a - ld [rNR42], a + ldh [rNR42], a swap a - ld [rNR44], a + ldh [rNR44], a .asm_f89a8 bit 2, d jr nz, .asm_f89b0 ld a, $0 - ld [rNR32], a + ldh [rNR32], a .asm_f89b0 ret diff --git a/src/audio/sfx.asm b/src/audio/sfx.asm index 6b3e2bc..c682191 100644 --- a/src/audio/sfx.asm +++ b/src/audio/sfx.asm @@ -400,7 +400,7 @@ SFX_7: ; fc202 (3f:4202) ld h, [hl] ld l, a ld a, $0 - ld [rNR30], a + ldh [rNR30], a ld b, d ld de, $ff30 .asm_fc215 @@ -414,7 +414,7 @@ SFX_7: ; fc202 (3f:4202) ld a, $1 ld [wMusicWaveChange], a ld a, $80 - ld [rNR30], a + ldh [rNR30], a ld b, $0 pop hl jp Func_fc094 @@ -481,14 +481,14 @@ Func_fc26c: ; fc26c (3f:426c) Func_fc279: ; fc279 (3f:4279) ld a, $8 - ld a, [rNR12] - ld a, [rNR22] - ld a, [rNR32] - ld a, [rNR42] + ldh a, [rNR12] + ldh a, [rNR22] + ldh a, [rNR32] + ldh a, [rNR42] ld a, $80 - ld a, [rNR14] - ld a, [rNR24] - ld a, [rNR44] + ldh a, [rNR14] + ldh a, [rNR24] + ldh a, [rNR44] xor a ld [wdd8c], a ret diff --git a/src/engine/bank04.asm b/src/engine/bank04.asm index a1e504f..8fb4d29 100644 --- a/src/engine/bank04.asm +++ b/src/engine/bank04.asm @@ -13,8 +13,8 @@ Func_10000: ; 10000 (4:4000) bit LCDC_ENABLE_F, a jr nz, .asm_10025 xor a - ld [rSCX], a - ld [rSCY], a + ldh [rSCX], a + ldh [rSCY], a .asm_10025 call Func_1288c diff --git a/src/engine/bank1c.asm b/src/engine/bank1c.asm index cff0a96..b8ac4fb 100644 --- a/src/engine/bank1c.asm +++ b/src/engine/bank1c.asm @@ -176,13 +176,13 @@ Func_70136: ; 70136 (1c:4136) or LCDC_BGON ld [wLCDC], a ld a, %11100100 - ld [rBGP], a + ldh [rBGP], a call SetBGP xor a ldh [hSCX], a - ld [rSCX], a + ldh [rSCX], a ldh [hSCY], a - ld [rSCY], a + ldh [rSCY], a pop de pop bc pop hl diff --git a/src/engine/home.asm b/src/engine/home.asm index ff887fd..f33ec2f 100644 --- a/src/engine/home.asm +++ b/src/engine/home.asm @@ -39,8 +39,8 @@ Start: ; 0150 (0:0150) ld sp, $fffe push af xor a - ld [rIF], a - ld [rIE], a + ldh [rIF], a + ldh [rIE], a call ZeroRAM ld a, $1 call BankswitchROM @@ -87,16 +87,16 @@ VBlankHandler: ; 019b (0:019b) .no_oam_copy ; flush scaling/windowing parameters ldh a, [hSCX] - ld [rSCX], a + ldh [rSCX], a ldh a, [hSCY] - ld [rSCY], a + ldh [rSCY], a ldh a, [hWX] - ld [rWX], a + ldh [rWX], a ldh a, [hWY] - ld [rWY], a + ldh [rWY], a ; flush LCDC ld a, [wLCDC] - ld [rLCDC], a + ldh [rLCDC], a ei call wVBlankFunctionTrampoline call FlushPalettesIfRequested @@ -186,17 +186,17 @@ SetupTimer: ; 0241 (0:0241) ld b, -68 ; Value for Normal Speed call CheckForCGB jr c, .set_timer - ld a, [rKEY1] + ldh a, [rKEY1] and $80 jr z, .set_timer ld b, $100 - 2 * 68 ; Value for CGB Double Speed .set_timer ld a, b - ld [rTMA], a + ldh [rTMA], a ld a, TAC_16384_HZ - ld [rTAC], a + ldh [rTAC], a ld a, TAC_START | TAC_16384_HZ - ld [rTAC], a + ldh [rTAC], a ret ; return carry if not CGB @@ -231,36 +231,36 @@ EnableLCD: ; 0277 (0:0277) ret nz ; assert that LCD is off or LCDC_ON ; ld [wLCDC], a ; - ld [rLCDC], a ; turn LCD on + ldh [rLCDC], a ; turn LCD on ld a, FLUSH_ALL_PALS ld [wFlushPaletteFlags], a ret ; wait for vblank, then turn LCD off DisableLCD: ; 028a (0:028a) - ld a, [rLCDC] ; + ldh a, [rLCDC] ; bit LCDC_ENABLE_F, a ; ret z ; assert that LCD is on - ld a, [rIE] + ldh a, [rIE] ld [wIE], a res INT_VBLANK, a ; - ld [rIE], a ; disable vblank interrupt + ldh [rIE], a ; disable vblank interrupt .wait_vblank - ld a, [rLY] ; + ldh a, [rLY] ; cp LY_VBLANK ; jr nz, .wait_vblank ; wait for vblank - ld a, [rLCDC] ; + ldh a, [rLCDC] ; and LCDC_OFF ; - ld [rLCDC], a ; + ldh [rLCDC], a ; ld a, [wLCDC] ; and LCDC_OFF ; ld [wLCDC], a ; turn LCD off xor a - ld [rBGP], a - ld [rOBP0], a - ld [rOBP1], a + ldh [rBGP], a + ldh [rOBP0], a + ldh [rOBP1], a ld a, [wIE] - ld [rIE], a + ldh [rIE], a ret ; set OBJ size: 8x8 @@ -293,50 +293,50 @@ Set_WD_off: ; 02d4 (0:02d4) ; enable timer interrupt EnableInt_Timer: ; 02dd (0:02dd) - ld a, [rIE] + ldh a, [rIE] or 1 << INT_TIMER - ld [rIE], a + ldh [rIE], a ret ; enable vblank interrupt EnableInt_VBlank: ; 02e4 (0:02e4) - ld a, [rIE] + ldh a, [rIE] or 1 << INT_VBLANK - ld [rIE], a + ldh [rIE], a ret ; enable lcdc interrupt on hblank mode EnableInt_HBlank: ; 02eb (0:02eb) - ld a, [rSTAT] + ldh a, [rSTAT] or 1 << STAT_MODE_HBLANK - ld [rSTAT], a + ldh [rSTAT], a xor a - ld [rIF], a - ld a, [rIE] + ldh [rIF], a + ldh a, [rIE] or 1 << INT_LCD_STAT - ld [rIE], a + ldh [rIE], a ret ; disable lcdc interrupt and the hblank mode trigger DisableInt_HBlank: ; 02fb (0:02fb) - ld a, [rSTAT] + ldh a, [rSTAT] and ~(1 << STAT_MODE_HBLANK) - ld [rSTAT], a + ldh [rSTAT], a xor a - ld [rIF], a - ld a, [rIE] + ldh [rIF], a + ldh a, [rIE] and ~(1 << INT_LCD_STAT) - ld [rIE], a + ldh [rIE], a ret ; initialize scroll, window, and lcdc registers, set trampoline functions ; for the lcdc and vblank interrupts, latch clock data, and enable SRAM/RTC SetupRegisters: ; 030b (0:030b) xor a - ld [rSCY], a - ld [rSCX], a - ld [rWY], a - ld [rWX], a + ldh [rSCY], a + ldh [rSCX], a + ldh [rWY], a + ldh [rWX], a ld [wcab0], a ld [wcab1], a ld [wcab2], a @@ -378,7 +378,7 @@ DetectConsole: ; 0349 (0:0349) cp CONSOLE_CGB ret nz ld a, $01 - ld [rSVBK], a + ldh [rSVBK], a call SwitchToCGBDoubleSpeed ret @@ -386,10 +386,10 @@ DetectConsole: ; 0349 (0:0349) SetupPalettes: ; 036a (0:036a) ld hl, wBGP ld a, %11100100 - ld [rBGP], a + ldh [rBGP], a ld [hli], a ; wBGP - ld [rOBP0], a - ld [rOBP1], a + ldh [rOBP0], a + ldh [rOBP1], a ld [hli], a ; wOBP0 ld [hl], a ; wOBP1 xor a @@ -545,11 +545,11 @@ FlushPalettesIfRequested: ; 042d (0:042d) ; flush grayscale (non-CGB) palettes ld hl, wBGP ld a, [hli] - ld [rBGP], a + ldh [rBGP], a ld a, [hli] - ld [rOBP0], a + ldh [rOBP0], a ld a, [hl] - ld [rOBP1], a + ldh [rOBP1], a ld a, [wConsole] cp CONSOLE_CGB jr z, .CGB @@ -600,7 +600,7 @@ CopyCGBPalettes: ; 0467 (0:0467) ld [$ff00+c], a inc c .wait - ld a, [rSTAT] + ldh a, [rSTAT] and 1 << STAT_BUSY ; wait until hblank or vblank jr nz, .wait ld a, [hl] @@ -679,21 +679,21 @@ BCCoordToBGMap0Address: ; 04cf (0:04cf) ; the A + B + Start + Select combination resets the game ReadJoypad: ; 04de (0:04de) ld a, JOY_BTNS_SELECT - ld [rJOYP], a - ld a, [rJOYP] - ld a, [rJOYP] + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] cpl and JOY_INPUT_MASK swap a ld b, a ; buttons data ld a, JOY_DPAD_SELECT - ld [rJOYP], a - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] cpl and JOY_INPUT_MASK or b @@ -726,7 +726,7 @@ SaveButtonsHeld: ld a, c ldh [hKeysHeld], a ld a, JOY_BTNS_SELECT | JOY_DPAD_SELECT - ld [rJOYP], a + ldh [rJOYP], a ret ; clear joypad hmem data @@ -824,7 +824,7 @@ CopyDMAFunction: ; 0593 (0:0593) ; CopyDMAFunction copies this function to hDMAFunction ($ff83) DMA: ; 05a1 (0:05a1) ld a, HIGH(wOAM) - ld [rDMA], a + ldh [rDMA], a ld a, $28 .wait dec a @@ -1322,7 +1322,7 @@ BankswitchVRAM0: ; 07c5 (0:07c5) push af xor a ldh [hBankVRAM], a - ld [rVBK], a + ldh [rVBK], a pop af ret @@ -1331,14 +1331,14 @@ BankswitchVRAM1: ; 07cd (0:07cd) push af ld a, $1 ldh [hBankVRAM], a - ld [rVBK], a + ldh [rVBK], a pop af ret ; set current dest VRAM bank to a BankswitchVRAM: ; 07d6 (0:07d6) ldh [hBankVRAM], a - ld [rVBK], a + ldh [rVBK], a ret ; switch to CGB Normal Speed Mode if playing on CGB and current mode is Double Speed Mode @@ -1361,20 +1361,20 @@ SwitchToCGBDoubleSpeed: ; 07e7 (0:07e7) ; switch between CGB Double Speed Mode and Normal Speed Mode CGBSpeedSwitch: ; 07f1 (0:07f1) - ld a, [rIE] + ldh a, [rIE] push af xor a - ld [rIE], a + ldh [rIE], a set 0, [hl] xor a - ld [rIF], a - ld [rIE], a + ldh [rIF], a + ldh [rIE], a ld a, $30 - ld [rJOYP], a + ldh [rJOYP], a stop call SetupTimer pop af - ld [rIE], a + ldh [rIE], a ret ; validate the saved data in SRAM @@ -1983,30 +1983,30 @@ DetectSGB: ; 0b59 (0:0b59) call Wait ld hl, MltReq2Packet call SendSGB - ld a, [rJOYP] + ldh a, [rJOYP] and %11 cp SNES_JOYPAD1 jr nz, .sgb ld a, P15 - ld [rJOYP], a - ld a, [rJOYP] - ld a, [rJOYP] + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] ld a, P15 | P14 - ld [rJOYP], a + ldh [rJOYP], a ld a, P14 - ld [rJOYP], a - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] ld a, P15 | P14 - ld [rJOYP], a - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] - ld a, [rJOYP] + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] and %11 cp SNES_JOYPAD1 jr nz, .sgb @@ -2037,13 +2037,13 @@ Func_0bcb: ; 0bcb (0:0bcb) di push de .wait_vbalnk - ld a, [rLY] + ldh a, [rLY] cp LY_VBLANK + 3 jr nz, .wait_vbalnk ld a, LCDC_BGON | LCDC_OBJON | LCDC_WIN9C00 - ld [rLCDC], a + ldh [rLCDC], a ld a, %11100100 - ld [rBGP], a + ldh [rBGP], a ld de, v0Tiles1 ld bc, v0BGMap0 - v0Tiles1 .tiles_loop @@ -2069,7 +2069,7 @@ Func_0bcb: ; 0bcb (0:0bcb) dec c jr nz, .bgmap_outer_loop ld a, LCDC_BGON | LCDC_OBJON | LCDC_WIN9C00 | LCDC_ON - ld [rLCDC], a + ldh [rLCDC], a pop hl call SendSGB ei @@ -2098,12 +2098,12 @@ HblankCopyDataHLtoDE: ; 0c19 (0:0c19) .loop ei di - ld a, [rSTAT] ; + ldh a, [rSTAT] ; and STAT_LCDC_STATUS ; jr nz, .loop ; assert hblank ld a, [hl] ld [de], a - ld a, [rSTAT] ; + ldh a, [rSTAT] ; and STAT_LCDC_STATUS ; jr nz, .loop ; assert still in hblank ei @@ -2120,12 +2120,12 @@ HblankCopyDataDEtoHL: ; 0c32 (0:0c32) .loop ei di - ld a, [rSTAT] ; + ldh a, [rSTAT] ; and STAT_LCDC_STATUS ; jr nz, .loop ; assert hblank ld a, [de] ld [hl], a - ld a, [rSTAT] ; + ldh a, [rSTAT] ; and STAT_LCDC_STATUS ; jr nz, .loop ; assert still in hblank ei @@ -2228,13 +2228,13 @@ SerialTimerHandler: ; 0c91 (0:0c91) jr z, .check_for_timeout ret .begin_transfer - ld a, [rSC] ; + ldh a, [rSC] ; add a ; make sure that no serial transfer is active ret c ; ld a, SC_INTERNAL - ld [rSC], a ; use internal clock + ldh [rSC], a ; use internal clock ld a, SC_START | SC_INTERNAL - ld [rSC], a ; use internal clock, set transfer start flag + ldh [rSC], a ; use internal clock, set transfer start flag ret .check_for_timeout ; sets bit7 of [wSerialFlags] if the serial interrupt hasn't triggered @@ -2273,11 +2273,11 @@ Func_0cc5: ; 0cc5 (0:0cc5) ret .asm_cdc ld a, $29 - ld [rSB], a + ldh [rSB], a ld a, SC_INTERNAL - ld [rSC], a + ldh [rSC], a ld a, SC_START | SC_INTERNAL - ld [rSC], a + ldh [rSC], a .asm_ce8 ld a, [hl] or a @@ -2330,17 +2330,17 @@ SerialHandler: ; 0d26 (0:0d26) or a ; jr z, .asm_d55 ; skip ahead if [wSerialOp] zero ; send/receive a byte - ld a, [rSB] + ldh a, [rSB] call SerialHandleRecv call SerialHandleSend ; returns byte to actually send push af .wait_for_completion - ld a, [rSC] + ldh a, [rSC] add a jr c, .wait_for_completion pop af ; end send/receive - ld [rSB], a ; prepare sending byte (from Func_0dc8?) + ldh [rSB], a ; prepare sending byte (from Func_0dc8?) ld a, [wSerialOp] cp $29 jr z, .done ; if [wSerialOp] != $29, use external clock @@ -2348,16 +2348,16 @@ SerialHandler: ; 0d26 (0:0d26) .asm_d55 ld a, $1 ld [wSerialRecvCounter], a - ld a, [rSB] + ldh a, [rSB] ld [wSerialRecvBuf], a ld a, $ac - ld [rSB], a + ldh [rSB], a ld a, [wSerialRecvBuf] cp $12 ; if [wSerialRecvBuf] != $12, use external clock jr z, .done ; and prepare for next byte. either way, return .asm_d6a ld a, SC_START | SC_EXTERNAL - ld [rSC], a ; transfer start, use external clock + ldh [rSC], a ; transfer start, use external clock .done ld hl, wSerialCounter inc [hl] @@ -2592,25 +2592,25 @@ SerialExchangeBytes: ; 0e63 (0:0e63) Func_0e8e: ; 0e8e (0:0e8e) call ClearSerialData ld a, $12 - ld [rSB], a ; send $12 + ldh [rSB], a ; send $12 ld a, SC_START | SC_EXTERNAL - ld [rSC], a ; use external clock, set transfer start flag - ld a, [rIF] + ldh [rSC], a ; use external clock, set transfer start flag + ldh a, [rIF] and ~(1 << INT_SERIAL) - ld [rIF], a ; clear serial interrupt flag - ld a, [rIE] + ldh [rIF], a ; clear serial interrupt flag + ldh a, [rIE] or 1 << INT_SERIAL ; enable serial interrupt - ld [rIE], a + ldh [rIE], a ret ; disable serial interrupt, and clear rSB, rSC, and serial registers in WRAM ResetSerial: ; 0ea6 (0:0ea6) - ld a, [rIE] + ldh a, [rIE] and ~(1 << INT_SERIAL) - ld [rIE], a + ldh [rIE], a xor a - ld [rSB], a - ld [rSC], a + ldh [rSB], a + ldh [rSC], a ; fallthrough ; zero serial registers in WRAM @@ -9400,14 +9400,14 @@ Func_31e5: ; 31e5 (0:31e5) jr Func_31e0 Func_31ea: ; 31ea (0:31ea) - ld a, [rSB] + ldh a, [rSB] ld [wce6e], a Func_31ef: ; 31ef (0:31ef) xor a jr Func_31e0 Func_31f2: ; 31f2 (0:31f2) - ld a, [rSB] + ldh a, [rSB] ld [wce6f], a xor a ld [wce63], a @@ -9434,11 +9434,11 @@ Func_31fc: ; 31fc (0:31fc) ; fallthrough Func_3212: ; 3212 (0:3212) - ld [rSB], a + ldh [rSB], a ld a, SC_INTERNAL - ld [rSC], a + ldh [rSC], a ld a, SC_START | SC_INTERNAL - ld [rSC], a + ldh [rSC], a ret ; doubles the damage at de if swords dance or focus energy was used @@ -11255,7 +11255,7 @@ CallBC: ; 3c46 (0:3c46) DoFrameIfLCDEnabled: ; 3c48 (0:3c48) push af - ld a, [rLCDC] + ldh a, [rLCDC] bit LCDC_ENABLE_F, a jr z, .done push bc diff --git a/src/gfx/cards/grassenergy.8x6.png b/src/gfx/cards/grassenergy.8x6.png Binary files differindex 2046bcb..5cc6b30 100644 --- a/src/gfx/cards/grassenergy.8x6.png +++ b/src/gfx/cards/grassenergy.8x6.png diff --git a/src/gfx/duel/cgb_symbols.t7.png b/src/gfx/duel/cgb_symbols.t7.png Binary files differindex 578867f..ac0f54d 100644 --- a/src/gfx/duel/cgb_symbols.t7.png +++ b/src/gfx/duel/cgb_symbols.t7.png diff --git a/src/gfx/duel/dmg_sgb_symbols.t7.png b/src/gfx/duel/dmg_sgb_symbols.t7.png Binary files differindex 10715db..91eede5 100644 --- a/src/gfx/duel/dmg_sgb_symbols.t7.png +++ b/src/gfx/duel/dmg_sgb_symbols.t7.png diff --git a/src/gfx/duel/other.t7.png b/src/gfx/duel/other.t7.png Binary files differindex 796ecf7..d2aa1e9 100644 --- a/src/gfx/duel/other.t7.png +++ b/src/gfx/duel/other.t7.png diff --git a/src/gfx/fonts/full_width/4.t3.png b/src/gfx/fonts/full_width/4.t3.png Binary files differindex 6ccef5d..c872a6d 100644 --- a/src/gfx/fonts/full_width/4.t3.png +++ b/src/gfx/fonts/full_width/4.t3.png diff --git a/src/gfx/fonts/symbols.png b/src/gfx/fonts/symbols.png Binary files differindex 81afc3b..347001a 100644 --- a/src/gfx/fonts/symbols.png +++ b/src/gfx/fonts/symbols.png diff --git a/tcg.link b/src/layout.link index 49511eb..49511eb 100644 --- a/tcg.link +++ b/src/layout.link diff --git a/src/rgbdscheck.asm b/src/rgbdscheck.asm new file mode 100644 index 0000000..622023d --- /dev/null +++ b/src/rgbdscheck.asm @@ -0,0 +1,12 @@ +; poketcg requires rgbds 0.4.1 or newer. +MAJOR EQU 0 +MINOR EQU 4 +PATCH EQU 1 + +IF !DEF(__RGBDS_MAJOR__) || !DEF(__RGBDS_MINOR__) || !DEF(__RGBDS_PATCH__) + fail "poketcg requires rgbds {MAJOR}.{MINOR}.{PATCH} or newer." +ELIF (__RGBDS_MAJOR__ < MAJOR) || \ + (__RGBDS_MAJOR__ == MAJOR && __RGBDS_MINOR__ < MINOR) || \ + (__RGBDS_MAJOR__ == MAJOR && __RGBDS_MINOR__ == MINOR && __RGBDS_PATCH__ < PATCH) + fail "poketcg requires rgbds {MAJOR}.{MINOR}.{PATCH} or newer." +ENDC diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..93050b9 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,2 @@ +scan_includes +gfx diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..c235fee --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,16 @@ +.PHONY: all clean + +CC := gcc +CFLAGS := -O3 -std=c99 -Wall -Wextra -pedantic + +tools := scan_includes gfx + +all: $(tools) + @: + +clean: + rm -f $(tools) + +gfx: common.h +%: %.c + $(CC) $(CFLAGS) -o $@ $< diff --git a/tools/common.h b/tools/common.h new file mode 100644 index 0000000..4da0b2e --- /dev/null +++ b/tools/common.h @@ -0,0 +1,40 @@ +#ifndef GUARD_COMMON_H +#define GUARD_COMMON_H + +int __getopt_long_i__; +#define getopt_long(c, v, s, l) getopt_long(c, v, s, l, &__getopt_long_i__) + +FILE *fopen_verbose(char *filename, char *mode) { + FILE *f = fopen(filename, mode); + if (!f) { + fprintf(stderr, "Could not open file: \"%s\"\n", filename); + } + return f; +} + +uint8_t *read_u8(char *filename, int *size) { + FILE *f = fopen_verbose(filename, "rb"); + if (!f) { + exit(1); + } + fseek(f, 0, SEEK_END); + *size = ftell(f); + rewind(f); + uint8_t *data = malloc(*size); + if (*size != (int)fread(data, 1, *size, f)) { + fprintf(stderr, "Could not read file: \"%s\"\n", filename); + exit(1); + } + fclose(f); + return data; +} + +void write_u8(char *filename, uint8_t *data, int size) { + FILE *f = fopen_verbose(filename, "wb"); + if (f) { + fwrite(data, 1, size, f); + fclose(f); + } +} + +#endif // GUARD_COMMON_H diff --git a/tools/gfx.c b/tools/gfx.c new file mode 100644 index 0000000..7d6dd0e --- /dev/null +++ b/tools/gfx.c @@ -0,0 +1,345 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <getopt.h> +#include <string.h> +#include <stdint.h> + +#include "common.h" + +static void usage(void) { + fprintf(stderr, "Usage: gfx [--trim-whitespace] [--remove-whitespace] [--interleave] [--remove-duplicates [--keep-whitespace]] [--remove-xflip] [--remove-yflip] [--preserve indexes] [--png filename] [-d depth] [-h] [-o outfile] infile\n"); +} + +static void error(char *message) { + fputs(message, stderr); + fputs("\n", stderr); +} + +struct Options { + int trim_whitespace; + int remove_whitespace; + int help; + char *outfile; + int depth; + int interleave; + int remove_duplicates; + int keep_whitespace; + int remove_xflip; + int remove_yflip; + int *preserved; + int num_preserved; + char *png_file; +}; + +struct Options Options = { + .depth = 2, +}; + +void get_args(int argc, char *argv[]) { + struct option long_options[] = { + {"remove-whitespace", no_argument, &Options.remove_whitespace, 1}, + {"trim-whitespace", no_argument, &Options.trim_whitespace, 1}, + {"interleave", no_argument, &Options.interleave, 1}, + {"remove-duplicates", no_argument, &Options.remove_duplicates, 1}, + {"keep-whitespace", no_argument, &Options.keep_whitespace, 1}, + {"remove-xflip", no_argument, &Options.remove_xflip, 1}, + {"remove-yflip", no_argument, &Options.remove_yflip, 1}, + {"preserve", required_argument, 0, 'r'}, + {"png", required_argument, 0, 'p'}, + {"depth", required_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {0} + }; + char *token; + for (int opt = 0; opt != -1;) { + switch (opt = getopt_long(argc, argv, "ho:d:p:", long_options)) { + case 'h': + Options.help = true; + break; + case 'o': + Options.outfile = optarg; + break; + case 'd': + Options.depth = strtoul(optarg, NULL, 0); + break; + case 'r': + token = strtok(optarg, ","); + while (token) { + Options.num_preserved++; + Options.preserved = realloc(Options.preserved, Options.num_preserved * sizeof(int)); + Options.preserved[Options.num_preserved-1] = strtoul(token, NULL, 0); + token = strtok(NULL, ","); + } + break; + case 'p': + Options.png_file = optarg; + break; + case 0: + case -1: + break; + default: + usage(); + exit(1); + break; + } + } +} + +bool is_preserved(int index) { + for (int i = 0; i < Options.num_preserved; i++) { + if (Options.preserved[i] == index) { + return true; + } + } + return false; +} + +void shift_preserved(int removed_index) { + for (int i = 0; i < Options.num_preserved; i++) { + if (Options.preserved[i] >= removed_index) { + Options.preserved[i]--; + } + } +} + +struct Graphic { + int size; + uint8_t *data; +}; + +bool is_whitespace(uint8_t *tile, int tile_size) { + uint8_t WHITESPACE = 0; + for (int i = 0; i < tile_size; i++) { + if (tile[i] != WHITESPACE) { + return false; + } + } + return true; +} + +void trim_whitespace(struct Graphic *graphic) { + int tile_size = Options.depth * 8; + for (int i = graphic->size - tile_size; i > 0; i -= tile_size) { + if (is_whitespace(&graphic->data[i], tile_size) && !is_preserved(i / tile_size)) { + graphic->size = i; + } else { + break; + } + } +} + +void remove_whitespace(struct Graphic *graphic) { + int tile_size = Options.depth * 8; + if (Options.interleave) tile_size *= 2; + + // Make sure we have a whole number of tiles, round down if required + graphic->size &= ~(tile_size - 1); + + int i = 0; + for (int j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) { + while (j < graphic->size && is_whitespace(&graphic->data[j], tile_size) && !is_preserved(j / tile_size - d)) { + shift_preserved(j / tile_size - d); + d++; + j += tile_size; + } + if (j >= graphic->size) { + break; + } + if (j > i) { + memcpy(&graphic->data[i], &graphic->data[j], tile_size); + } + } + graphic->size = i; +} + +bool tile_exists(uint8_t *tile, uint8_t *tiles, int tile_size, int num_tiles) { + for (int i = 0; i < num_tiles; i++) { + bool match = true; + for (int j = 0; j < tile_size; j++) { + if (tile[j] != tiles[i * tile_size + j]) { + match = false; + } + } + if (match) { + return true; + } + } + return false; +} + +void remove_duplicates(struct Graphic *graphic) { + int tile_size = Options.depth * 8; + if (Options.interleave) tile_size *= 2; + int num_tiles = 0; + + // Make sure we have a whole number of tiles, round down if required + graphic->size &= ~(tile_size - 1); + + for (int i = 0, j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) { + while (j < graphic->size && tile_exists(&graphic->data[j], graphic->data, tile_size, num_tiles)) { + if ((Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) || is_preserved(j / tile_size - d)) { + break; + } + shift_preserved(j / tile_size - d); + d++; + j += tile_size; + } + if (j >= graphic->size) { + break; + } + if (j > i) { + memcpy(&graphic->data[i], &graphic->data[j], tile_size); + } + num_tiles++; + } + graphic->size = num_tiles * tile_size; +} + +bool flip_exists(uint8_t *tile, uint8_t *tiles, int tile_size, int num_tiles, bool xflip, bool yflip) { + uint8_t flip[tile_size]; + memset(flip, 0, sizeof(flip)); + int half_size = tile_size / 2; + for (int i = 0; i < tile_size; i++) { + int byte = i; + if (yflip) { + byte = tile_size - 1 - (i ^ 1); + if (Options.interleave && i < half_size) { + byte = half_size - 1 - (i ^ 1); + } + } + if (xflip) { + for (int bit = 0; bit < 8; bit++) { + flip[byte] |= ((tile[i] >> bit) & 1) << (7 - bit); + } + } else { + flip[byte] = tile[i]; + } + } + if (tile_exists(flip, tiles, tile_size, num_tiles)) { + return true; + } + return false; +} + +void remove_flip(struct Graphic *graphic, bool xflip, bool yflip) { + int tile_size = Options.depth * 8; + if (Options.interleave) tile_size *= 2; + int num_tiles = 0; + + // Make sure we have a whole number of tiles, round down if required + graphic->size &= ~(tile_size - 1); + + for (int i = 0, j = 0, d = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) { + while (j < graphic->size && flip_exists(&graphic->data[j], graphic->data, tile_size, num_tiles, xflip, yflip)) { + if ((Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) || is_preserved(j / tile_size - d)) { + break; + } + shift_preserved(j / tile_size - d); + d++; + j += tile_size; + } + if (j >= graphic->size) { + break; + } + if (j > i) { + memcpy(&graphic->data[i], &graphic->data[j], tile_size); + } + num_tiles++; + } + graphic->size = num_tiles * tile_size; +} + +void interleave(struct Graphic *graphic, int width) { + int tile_size = Options.depth * 8; + int width_tiles = width / 8; + int num_tiles = graphic->size / tile_size; + uint8_t *interleaved = malloc(graphic->size); + for (int i = 0; i < num_tiles; i++) { + int tile = i * 2; + int row = i / width_tiles; + tile -= width_tiles * row; + if (row % 2) { + tile -= width_tiles; + tile += 1; + } + memcpy(&interleaved[tile * tile_size], &graphic->data[i * tile_size], tile_size); + } + graphic->size = num_tiles * tile_size; + memcpy(graphic->data, interleaved, graphic->size); + free(interleaved); +} + +int png_get_width(char *filename) { + FILE *f = fopen_verbose(filename, "rb"); + if (!f) { + exit(1); + } + + const int OFFSET_WIDTH = 16; + uint8_t bytes[4]; + fseek(f, OFFSET_WIDTH, SEEK_SET); + size_t size = 4; + size_t result = fread(bytes, 1, size, f); + fclose(f); + if (result != size) { + fprintf(stderr, "Could not read file at offset 0x%x: \"%s\"\n", OFFSET_WIDTH, filename); + exit(1); + } + + int width = 0; + for (int i = 0; i < 4; i++) { + width |= bytes[i] << (8 * (3 - i)); + } + return width; +} + + +int main(int argc, char *argv[]) { + get_args(argc, argv); + argc -= optind; + argv += optind; + if (Options.help) { + usage(); + return 0; + } + if (argc < 1) { + usage(); + exit(1); + } + char *infile = argv[0]; + struct Graphic graphic; + graphic.data = read_u8(infile, &graphic.size); + if (Options.trim_whitespace) { + trim_whitespace(&graphic); + } + if (Options.interleave) { + if (!Options.png_file) { + error("interleave: need --png to infer dimensions"); + usage(); + exit(1); + } + int width = png_get_width(Options.png_file); + interleave(&graphic, width); + } + if (Options.remove_duplicates) { + remove_duplicates(&graphic); + } + if (Options.remove_xflip) { + remove_flip(&graphic, true, false); + } + if (Options.remove_yflip) { + remove_flip(&graphic, false, true); + } + if (Options.remove_xflip && Options.remove_yflip) { + remove_flip(&graphic, true, true); + } + if (Options.remove_whitespace) { + remove_whitespace(&graphic); + } + if (Options.outfile) { + write_u8(Options.outfile, graphic.data, graphic.size); + } + free(graphic.data); + return 0; +} diff --git a/tools/scan_includes.c b/tools/scan_includes.c new file mode 100644 index 0000000..0a6230b --- /dev/null +++ b/tools/scan_includes.c @@ -0,0 +1,197 @@ +#define _DEFAULT_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <getopt.h> +#include <limits.h> + +void usage(void) { + printf("Usage: scan_includes [-h] [-s] [-i path] filename\n" + "-h, --help\n" + " Print usage and exit\n" + "-s, --strict\n" + " Fail if a file cannot be read\n" + "-i, --include\n" + " Add an include path\n"); +} + +struct Options { + bool help; + bool strict; + char **include_paths; + int include_paths_len; +}; + +struct Options Options = {0}; + +void *xmalloc(const size_t size) +{ + void *ptr = malloc(size); + if (!ptr) { + perror("malloc"); + exit(1); + } + return ptr; +} + +void options_add_file(char *fname) +{ + char *filename = xmalloc(strlen(fname) + 1); + strcpy(filename, fname); + if (!(Options.include_paths = realloc(Options.include_paths, + sizeof(Options.include_paths[0]) * ++Options.include_paths_len))) { + perror("realloc"); + exit(1); + } + Options.include_paths[Options.include_paths_len - 1] = filename; +} + +void scan_file(char *filename) { + FILE *f = fopen(filename, "r"); + + if (!f) { + if (Options.strict) { + fprintf(stderr, "Could not open file: '%s'\n", filename); + exit(1); + } else { + return; + } + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + rewind(f); + + char *buffer = xmalloc(size + 1); + char *orig = buffer; + size = fread(buffer, 1, size, f); + buffer[size] = '\0'; + fclose(f); + + for (; buffer && (buffer - orig < size); buffer++) { + bool is_include = false; + bool is_incbin = false; + switch (*buffer) { + case ';': + buffer = strchr(buffer, '\n'); + if (!buffer) { + fprintf(stderr, "%s: no newline at end of file\n", filename); + break; + } + break; + + case '"': + buffer++; + buffer = strchr(buffer, '"'); + if (!buffer) { + fprintf(stderr, "%s: unterminated string\n", filename); + break; + } + buffer++; + break; + + case 'i': + case 'I': + if ((strncmp(buffer, "INCBIN", 6) == 0) || (strncmp(buffer, "incbin", 6) == 0)) { + is_incbin = true; + } else if ((strncmp(buffer, "INCLUDE", 7) == 0) || (strncmp(buffer, "include", 7) == 0)) { + is_include = true; + } + if (is_incbin || is_include) { + buffer = strchr(buffer, '"'); + if (!buffer) { + break; + } + buffer++; + int length = strcspn(buffer, "\""); + char *include = xmalloc(length + 1); + strncpy(include, buffer, length); + include[length] = '\0'; + + f = fopen(include, "r"); + char *path = NULL; + if (!f) { + for (char **include_path = Options.include_paths; + include_path < Options.include_paths + Options.include_paths_len; + include_path++) { + size_t len = strlen(*include_path) + strlen(include) + 1; + path = xmalloc(len); + snprintf(path, len, "%s%s", *include_path, include); + f = fopen(path, "r"); + if (f) break; + free(path); + path = NULL; + } + if (!path && is_incbin && Options.include_paths_len == 1) { + char **include_path = Options.include_paths; + size_t len = strlen(*include_path) + strlen(include) + 1; + path = xmalloc(len); + snprintf(path, len, "%s%s", *include_path, include); + } + } + if (f) fclose(f); + + if (path) { + free(include); + include = path; + } + printf("%s ", include); + if (is_include) { + scan_file(include); + } + free(include); + buffer = strchr(buffer, '"'); + } + break; + + } + if (!buffer) { + break; + } + + } + + free(orig); +} + +int main(int argc, char* argv[]) { + int i = 0; + struct option long_options[] = { + {"strict", no_argument, 0, 's'}, + {"help", no_argument, 0, 'h'}, + {"include", required_argument, 0, 'i'}, + {0} + }; + int opt = -1; + while ((opt = getopt_long(argc, argv, "shi:", long_options, &i)) != -1) { + switch (opt) { + case 's': + Options.strict = true; + break; + case 'h': + Options.help = true; + break; + case 'i': + options_add_file(optarg); + break; + default: + usage(); + exit(1); + break; + } + } + argc -= optind; + argv += optind; + if (Options.help) { + usage(); + return 0; + } + if (argc < 1) { + usage(); + exit(1); + } + scan_file(argv[0]); + return 0; +} |