1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
|
One of Pokémon Crystal's improvements to Gold and Silver was animating the Pokémon sprites. There are various reasons why you might want to remove this feature:
- You're changing a lot of Pokémon and don't want to design animations for them all
- You want the game to more closely resemble R/B/Y or G/S
- You need the space for something else
If you just want some of the Pokémon to not be animated, then as mentioned in the [new Pokémon tutorial](Add-a-new-Pokémon), you can just put `endanim` as the full contents of anim.asm and anim_idle.asm for those particular Pokémon. But if none if them will be animated, then you can save considerable ROM space by removing the entire animation system.
## Contents
1. [Delete the sprite animation system and data](#1-delete-the-sprite-animation-system-and-data)
2. [Remove references to deleted files](#2-remove-references-to-deleted-files)
3. [Remove code and data that is no longer used](#3-remove-code-and-data-that-is-no-longer-used)
4. [Don't load animation graphics](#4-dont-load-animation-graphics)
5. [Play cries instead of animations](#5-play-cries-instead-of-animations)
6. [Fix the Pokémon stats screen](#6-fix-the-pokémon-stats-screen)
7. [Remove the unused animation tile graphics](#7-remove-the-unused-animation-tile-graphics)
## 1. Delete the sprite animation system and data
Delete all of these files:
- [engine/gfx/pic_animation.asm](../blob/master/engine/gfx/pic_animation.asm)
- [gfx/pokemon/anim_pointers.asm](../blob/master/gfx/pokemon/anim_pointers.asm)
- [gfx/pokemon/anims.asm](../blob/master/gfx/pokemon/anims.asm)
- [gfx/pokemon/idle_pointers.asm](../blob/master/gfx/pokemon/idle_pointers.asm)
- [gfx/pokemon/idles.asm](../blob/master/gfx/pokemon/idles.asm)
- [gfx/pokemon/bitmask_pointers.asm](../blob/master/gfx/pokemon/bitmask_pointers.asm)
- [gfx/pokemon/bitmasks.asm](../blob/master/gfx/pokemon/bitmasks.asm)
- [gfx/pokemon/frame_pointers.asm](../blob/master/gfx/pokemon/frame_pointers.asm)
- [gfx/pokemon/johto_frames.asm](../blob/master/gfx/pokemon/johto_frames.asm)
- [gfx/pokemon/kanto_frames.asm](../blob/master/gfx/pokemon/kanto_frames.asm)
- [gfx/pokemon/unown_anim_pointers.asm](../blob/master/gfx/pokemon/unown_anim_pointers.asm)
- [gfx/pokemon/unown_anims.asm](../blob/master/gfx/pokemon/unown_anims.asm)
- [gfx/pokemon/unown_idle_pointers.asm](../blob/master/gfx/pokemon/unown_idle_pointers.asm)
- [gfx/pokemon/unown_idles.asm](../blob/master/gfx/pokemon/unown_idles.asm)
- [gfx/pokemon/unown_bitmask_pointers.asm](../blob/master/gfx/pokemon/unown_bitmask_pointers.asm)
- [gfx/pokemon/unown_bitmasks.asm](../blob/master/gfx/pokemon/unown_bitmasks.asm)
- [gfx/pokemon/unown_frame_pointers.asm](../blob/master/gfx/pokemon/unown_frame_pointers.asm)
- [gfx/pokemon/unown_frames.asm](../blob/master/gfx/pokemon/unown_frames.asm)
- [gfx/pokemon/unown/bitmask.asm](../blob/master/gfx/pokemon/unown/bitmask.asm)
- [gfx/pokemon/unown/frames.asm](../blob/master/gfx/pokemon/unown/frames.asm)
Then run these commands to delete files that might have been created by `make`:
```bash
rm -f gfx/pokemon/*/front.animated.2bpp
rm -f gfx/pokemon/*/front.animated.2bpp.lz
rm -f gfx/pokemon/*/front.animated.tilemap
rm -f gfx/pokemon/*/bitmask.asm
rm -f gfx/pokemon/*/frames.asm
```
## 2. Remove references to deleted files
Edit [main.asm](../blob/master/main.asm):
```diff
-SECTION "Pic Animations 1", ROMX
-
-INCLUDE "engine/gfx/pic_animation.asm"
-...
-INCLUDE "gfx/pokemon/unown_bitmasks.asm"
-
-
-SECTION "Pic Animations 2", ROMX
-
-INCLUDE "gfx/pokemon/frame_pointers.asm"
-INCLUDE "gfx/pokemon/kanto_frames.asm"
-
-
SECTION "Font Inversed", ROMX
FontInversed:
INCBIN "gfx/font/font_inversed.1bpp"
-
-
-SECTION "Pic Animations 3", ROMX
-
-INCLUDE "gfx/pokemon/johto_frames.asm"
-INCLUDE "gfx/pokemon/unown_frame_pointers.asm"
-INCLUDE "gfx/pokemon/unown_frames.asm"
```
And edit [pokecrystal.link](../blob/master/pokecrystal.link):
```diff
-ROMX $34
- "Pic Animations 1"
-ROMX $35
- "Pic Animations 2"
ROMX $36
"Font Inversed"
- "Pic Animations 3"
```
## 3. Remove code and data that is no longer used
Edit [wram.asm](../blob/master/wram.asm):
```diff
-; PokeAnim data
-wPokeAnimStruct::
-wPokeAnimSceneIndex:: db
-...
-wPokeAnimStructEnd::
```
Edit [constants/gfx_constants.asm](../blob/master/constants/gfx_constants.asm):
```diff
-; PokeAnims indexes (see engine/gfx/pic_animation.asm)
- const_def
- const ANIM_MON_SLOW
- ...
- const ANIM_MON_EGG2
```
Edit [data/predef_pointers.asm](../blob/master/data/predef_pointers.asm):
```diff
PredefPointers::
...
- add_predef Unused_AnimateMon_Slow_Normal
add_predef PlaceStatusString
- add_predef LoadMonAnimation
- add_predef AnimateFrontpic
- add_predef Unused_HOF_AnimateAlignedFrontpic ; $48
- add_predef HOF_AnimateFrontpic
- dbw -1, InexplicablyEmptyFunction ; ???
```
## 4. Don't load animation graphics
Edit [engine/gfx/load_pics.asm](../blob/master/engine/gfx/load_pics.asm):
```diff
GetAnimatedFrontpic:
ld a, [wCurPartySpecies]
ld [wCurSpecies], a
call IsAPokemon
ret c
ldh a, [rSVBK]
push af
xor a
ldh [hBGMapMode], a
call _GetFrontpic
- call GetAnimatedEnemyFrontpic
pop af
ldh [rSVBK], a
ret
...
-GetAnimatedEnemyFrontpic:
- ...
- ret
-
-LoadFrontpicTiles:
- ...
- ret
```
## 5. Play cries instead of animations
If you read [engine/gfx/pic_animation.asm](../blob/master/engine/gfx/pic_animation.asm) before deleting it, you'll see how the animation system works. Usually the `AnimateFrontpic` routine is called with an `ANIM_MON_*` value loaded into `e`. It then runs a series of subroutines based on the value of `e`, selected from the `PokeAnims` array:
```
PokeAnims:
; entries correspond to ANIM_MON_* constants
dw .Slow
dw .Normal
dw .Menu
dw .Trade
dw .Evolve
dw .Hatch
dw .HOF
dw .Egg1
dw .Egg2
.Slow: pokeanim StereoCry, Setup2, Play
.Normal: pokeanim StereoCry, Setup, Play
.Menu: pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
.Trade: pokeanim Idle, Play2, Idle, Play, SetWait, Wait, Cry, Setup, Play
.Evolve: pokeanim Idle, Play, SetWait, Wait, CryNoWait, Setup, Play
.Hatch: pokeanim Idle, Play, CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
.HOF: pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
.Egg1: pokeanim Setup, Play
.Egg2: pokeanim Idle, Play
```
Notice that not all of the subroutines are related to graphics: `Cry`, `CryNoWait`, and `StereoCry` play the audio cries. So since we've deleted the whole implementation of `AnimateFrontpic`, we have to restore the cry-playing functionality to wherever `AnimateFrontpic` was called.
Edit [engine/gfx/trademon_frontpic.asm](../blob/master/engine/gfx/trademon_frontpic.asm):
```diff
AnimateTrademonFrontpic:
...
ld a, [wOTTrademonSpecies]
ld [wCurPartySpecies], a
- hlcoord 7, 2
- ld d, $0
- ld e, ANIM_MON_TRADE
- predef AnimateFrontpic
- ret
+ jp PlayMonCry
```
Edit [engine/movie/evolution_animation.asm](../blob/master/engine/movie/evolution_animation.asm):
```diff
EvolutionAnimation:
...
ld a, [wPlayerHPPal]
ld [wCurPartySpecies], a
- hlcoord 7, 2
- ld d, $0
- ld e, ANIM_MON_EVOLVE
- predef AnimateFrontpic
+ call PlayMonCry2
pop af
ld [wCurPartySpecies], a
pop af
ld [wBoxAlignment], a
ret
```
Edit [engine/pokemon/breeding.asm](../blob/master/engine/pokemon/breeding.asm):
```diff
EggHatch_AnimationSequence:
...
ld a, [wJumptableIndex]
ld [wCurPartySpecies], a
- hlcoord 6, 3
- ld d, $0
- ld e, ANIM_MON_HATCH
- predef AnimateFrontpic
+ call PlayMonCry2
pop af
ld [wCurSpecies], a
ret
```
Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
```diff
Function_SetEnemyMonAndSendOutAnimation:
...
.not_shiny
ld bc, wTempMonSpecies
farcall CheckFaintedFrzSlp
jr c, .skip_cry
- farcall CheckBattleScene
- jr c, .cry_no_anim
-
- hlcoord 12, 0
- ld d, $0
- ld e, ANIM_MON_SLOW
- predef AnimateFrontpic
- jr .skip_cry
-
-.cry_no_anim
ld a, $f
ld [wCryTracks], a
ld a, [wTempEnemyMonSpecies]
call PlayStereoCry
.skip_cry
call UpdateEnemyHUD
ld a, $1
ldh [hBGMapMode], a
ret
...
BattleStartMessage:
...
.not_shiny
farcall CheckSleepingTreeMon
jr c, .skip_cry
- farcall CheckBattleScene
- jr c, .cry_no_anim
-
- hlcoord 12, 0
- ld d, $0
- ld e, ANIM_MON_NORMAL
- predef AnimateFrontpic
- jr .skip_cry ; cry is played during the animation
-
-.cry_no_anim
ld a, $f
ld [wCryTracks], a
ld a, [wTempEnemyMonSpecies]
call PlayStereoCry
.skip_cry
...
```
Turning the "Battle Scene" option off would already disable sprite animations here, so all we had to do was make its case the default and only one.
Edit [engine/events/halloffame.asm](../blob/master/engine/events/halloffame.asm):
```diff
AnimateHallOfFame:
...
.DisplayNewHallOfFamer:
call DisplayHOFMon
ld de, .String_NewHallOfFamer
hlcoord 1, 2
call PlaceString
call WaitBGMap
- decoord 6, 5
- ld c, ANIM_MON_HOF
- predef HOF_AnimateFrontpic
- ld c, 60
+ call HOF_PlayCry
+ ld c, 180
call DelayFrames
and a
ret
...
_HallOfFamePC:
...
.finish
ld de, .EmptyString
call PlaceString
call WaitBGMap
ld b, SCGB_PLAYER_OR_MON_FRONTPIC_PALS
call GetSGBLayout
call SetPalettes
- decoord 6, 5
- ld c, ANIM_MON_HOF
- predef HOF_AnimateFrontpic
+ call HOF_PlayCry
and a
ret
...
+HOF_PlayCry::
+ ld a, [wCurPartySpecies]
+ cp EGG
+ jr z, .fail
+ call IsAPokemon
+ jr c, .fail
+ ld a, [wCurPartySpecies]
+ call PlayMonCry2
+ ret
+
+.fail
+ ld a, 1
+ ld [wCurPartySpecies], a
+ ret
```
The Hall of Fame uses its own `HOF_AnimateFrontpic` instead of the usual `AnimateFrontpic`, so we copied its extra behavior. We also increased the delay for displaying new Hall of Famers from 60 frames to 180, the same as G/S, since sprite animations aren't contributing to the delay any more.
Edit [mobile/mobile_42.asm](../blob/master/mobile/mobile_42.asm):
```diff
Function108219:
ld [wCurPartySpecies], a
- hlcoord 7, 2
- ld d, $0
- ld e, ANIM_MON_TRADE
- predef AnimateFrontpic
- ret
+ jp PlayMonCry
Function108229:
ld [wCurPartySpecies], a
- hlcoord 7, 2
- ld d, $0
- ld e, ANIM_MON_TRADE
- predef LoadMonAnimation
ret
...
Function1082db:
.loop
farcall PlaySpriteAnimations
- farcall SetUpPokeAnim
- farcall HDMATransferTileMapToWRAMBank3
jr nc, .loop
ret
```
Finally, edit [mobile/mobile_5f.asm](../blob/master/mobile/mobile_5f.asm):
```diff
Function17d93a:
...
decoord 0, 0
add hl, de
ld e, l
ld d, h
- farcall HOF_AnimateFrontpic
+ farcall HOF_PlayCry
pop af
ldh [rSVBK], a
call Function17e349
ret
```
## 6. Fix the Pokémon stats screen
The stats screen doesn't call `AnimateFrontpic` *or* `HOF_AnimateFrontpic`. Instead, it calls individual low-level subroutines of the sprite animation engine. So fixing it to just play cries is a bit more tricky than with the other subsystems.
Edit [engine/pokemon/stats_screen.asm](../blob/master/engine/pokemon/stats_screen.asm):
```diff
StatsScreen_WaitAnim:
ld hl, wcf64
bit 6, [hl]
jr nz, .try_anim
bit 5, [hl]
jr nz, .finish
call DelayFrame
ret
.try_anim
- farcall SetUpPokeAnim
- jr nc, .finish
ld hl, wcf64
res 6, [hl]
.finish
ld hl, wcf64
res 5, [hl]
farcall HDMATransferTileMapToWRAMBank3
ret
...
StatsScreen_PlaceFrontpic:
ld hl, wTempMonDVs
predef GetUnownLetter
call StatsScreen_GetAnimationParam
jr c, .egg
and a
jr z, .no_cry
jr .cry
...
.get_animation
ld a, [wCurPartySpecies]
call IsAPokemon
ret c
call StatsScreen_LoadTextboxSpaceGFX
ld de, vTiles2 tile $00
predef GetAnimatedFrontpic
- hlcoord 0, 0
- ld d, $0
- ld e, ANIM_MON_MENU
- predef LoadMonAnimation
ld hl, wcf64
set 6, [hl]
ret
StatsScreen_GetAnimationParam:
ld a, [wMonType]
ld hl, .Jumptable
rst JumpTable
ret
.Jumptable:
dw .PartyMon
dw .OTPartyMon
dw .BoxMon
dw .Tempmon
dw .Wildmon
...
.CheckEggFaintedFrzSlp:
ld a, [wCurPartySpecies]
cp EGG
jr z, .egg
call CheckFaintedFrzSlp
jr c, .FaintedFrzSlp
+ jr .Wildmon
+
.egg
xor a
scf
ret
.Wildmon:
ld a, $1
and a
ret
.FaintedFrzSlp:
xor a
ret
...
StatsScreen_AnimateEgg:
...
- hlcoord 0, 0
- ld d, $0
- predef LoadMonAnimation
ld hl, wcf64
set 6, [hl]
ret
```
Before, the `farcall SetUpPokeAnim` in `StatsScreen_WaitAnim` would play the Pokémon cry. Since we deleted that, we have to fix `StatsScreen_GetAnimationParam.CheckEggFaintedFrzSlp` so that it will return `nz` and `nc` for Pokémon that are neither Eggs, nor fainted, nor frozen, nor asleep. That way `StatsScreen_PlaceFrontpic` will correctly play cries for those Pokémon.
## 7. Remove the unused animation tile graphics
If we stopped here, the animations would be gone and the cries would play correctly, but the ROM would still have leftover graphics data for the sprite animations.
One way to fix this would be to edit all 276 [gfx/pokemon/\*/front.png](../tree/master/gfx/pokemon/) files (251 Pokémon, with 26 forms for Unown), cropping each one to just be the first frame. But doing this manually is tedious, and doing it automatically (a) requires installing [GraphicsMagick](http://www.graphicsmagick.org/) and (b) risks messing up the images' [indexed color palettes](https://en.wikipedia.org/wiki/Indexed_color).
Another way is to crop the sprites *after* they've been converted from .png to .2bpp format. That's what we're going to do.
(If you're replacing the 251 Gen 2 Pokémon with your own set, then skip this step. And of course when you're drawing front sprites, just draw them as square still sprites, without any animated frames.)
Create **tools/trim_animation.sh**:
```sh
#!/bin/sh
# Usage: trim_animation.sh front.animated.2bpp front.dimensions
case $(cat $2) in
U) bytes=400;; # $55="U"; 5*5*16=400
f) bytes=576;; # $66="f"; 6*6*16=576
w) bytes=784;; # $77="w"; 7*7*16=784
*) bytes=-0;; # invalid size; don't trim
esac
temp_file=$(mktemp)
head -c $bytes $1 > $temp_file
mv $temp_file $1
```
(Be sure to use [Unix line endings](https://blog.codinghorror.com/the-great-newline-schism/), not DOS/Windows, just like all the other text files in pokecrystal!)
Then edit the [Makefile](../blob/master/Makefile):
```diff
### Pokemon pic animation rules
gfx/pokemon/%/front.animated.2bpp: gfx/pokemon/%/front.2bpp gfx/pokemon/%/front.dimensions
tools/pokemon_animation_graphics -o $@ $^
+ tools/trim_animation.sh $@ $(word 2,$^)
-gfx/pokemon/%/front.animated.tilemap: gfx/pokemon/%/front.2bpp gfx/pokemon/%/front.dimensions
- tools/pokemon_animation_graphics -t $@ $^
-gfx/pokemon/%/bitmask.asm: gfx/pokemon/%/front.animated.tilemap gfx/pokemon/%/front.dimensions
- tools/pokemon_animation -b $^ > $@
-gfx/pokemon/%/frames.asm: gfx/pokemon/%/front.animated.tilemap gfx/pokemon/%/front.dimensions
- tools/pokemon_animation -f $^ > $@
-
-
-### Terrible hacks to match animations. Delete these rules if you don't care about matching.
-
-# Dewgong has an unused tile id in its last frame. The tile itself is missing.
-gfx/pokemon/dewgong/frames.asm: gfx/pokemon/dewgong/front.animated.tilemap gfx/pokemon/dewgong/front.dimensions
- tools/pokemon_animation -f $^ > $@
- echo " db \$$4d" >> $@
-
-# Lugia has two unused tile ids in its last frame. The tiles themselves are missing.
-gfx/pokemon/lugia/frames.asm: gfx/pokemon/lugia/front.animated.tilemap gfx/pokemon/lugia/front.dimensions
- tools/pokemon_animation -f $^ > $@
- echo " db \$$5e, \$$59" >> $@
-
-# Girafarig has a redundant tile after the end. It is used in two frames, so it must be injected into the generated graphics.
-# This is more involved, so it's hacked into pokemon_animation_graphics.
-gfx/pokemon/girafarig/front.animated.2bpp: gfx/pokemon/girafarig/front.2bpp gfx/pokemon/girafarig/front.dimensions
- tools/pokemon_animation_graphics --girafarig -o $@ $^
-gfx/pokemon/girafarig/front.animated.tilemap: gfx/pokemon/girafarig/front.2bpp gfx/pokemon/girafarig/front.dimensions
- tools/pokemon_animation_graphics --girafarig -t $@ $^
```
(Be sure to use tabs, not spaces, for indenting commands in the Makefile!)
(Older versions of pokecrystal also had rules to build `gfx/pokemon/%/normal.pal`, `gfx/pokemon/%/normal.gbcpal`, and `gfx/pokemon/%/back.2bpp`, but as of September 2018 they were removed. They do not need to be changed for this tutorial.)
Let's go over what trim_animation.sh is doing. Before, this was the workflow when building the ROM:
1. `tools/gfx` converts each front.png to **front.2bpp**
2. `tools/png_dimensions` reads each front.png and writes its size as a single byte in **front.dimensions**
3. `tools/pokemon_animation_graphics` takes each pair of front.2bpp and front.dimensions and creates **front.animated.2bpp**, which is modified from front.2bpp in two ways:
- The first frame is transposed so its tiles are in column order, not row order
- The subsequent frames are treated as a series of *animation tiles*, and duplicate tiles are left out
4. `tools/lzcomp` compresses each front.animated.2bpp into **front.animated.2bpp.lz**
(Actually, if a **front.animated.2bpp.lz.XXXXXXXX** file exists, and the [MD5 hash](https://en.wikipedia.org/wiki/MD5) of front.animated.2bpp is XXXXXXXX, then it will just copy front.animated.2bpp.lz.XXXXXXXX as the compressed file. This is done because `tools/lzcomp` can't always reproduce the exact same compression as the original Pokémon Crystal ROM.)
Anyway, trim_animation.sh takes each front.animated.2bpp file and removes the animation tiles, keeping only the transposed tiles for the first frame. It reads front.dimensions to know how many tiles the first frames uses. Then each tile uses 16 bytes, so it keeps that many bytes of the head of the file.
That's it, we're done! This change frees up 102 KB of a 2 MB ROM: nearly 5% of the total space. Considering that an original Crystal ROM has 20% of its space unused, we've increased the free space by nearly 25%.
(As of July 23, 2018, you can measure free space after building a ROM with `make` by running `tools/free_space.awk pokecrystal.map`. The script [tools/free_space.awk](../blob/master/tools/free_space.awk) parses a .map file and adds up all the unused "slack" space.)
|