mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-06 17:10:55 -04:00
1106 lines
48 KiB
Lua
1106 lines
48 KiB
Lua
-- Engine-owned contract for generated ROM caches.
|
|
--
|
|
-- A cache is playable only when its versioned marker matches the ROM and every
|
|
-- required output for that version exists. Extraction writers may differ by
|
|
-- platform, but they must publish through this contract so partial staging
|
|
-- cannot look ready to the runtime.
|
|
local GameVersion = require("src.core.GameVersion")
|
|
|
|
local CacheContract = {}
|
|
|
|
-- engine/battle/animations.asm:2418
|
|
CacheContract.FORMAT = "rom-cache-v12-gen1:"
|
|
CacheContract.VERSION_FORMAT = {
|
|
-- v11: Gen 2 maps carry their object list's ROM address, which a .sav
|
|
-- export re-anchoring a save onto another map writes back into
|
|
-- wCurMapObjectEventsPointer. A v10 cache has no address to write, and
|
|
-- such an export is refused until the ROM re-imports.
|
|
gold = "rom-cache-v13:",
|
|
silver = "rom-cache-v13:",
|
|
crystal = "rom-cache-v13-crystal6:",
|
|
-- engine/overworld/map_sprites.asm:181, engine/battle/animations.asm:2600
|
|
-- data/pikachu/pikachu_pic_animation.asm:340
|
|
yellow = "rom-cache-v12-yellow2:",
|
|
-- v8: M4A tracks retain reachable patterns and explicit entry offsets.
|
|
firered = "rom-cache-v24-firered:",
|
|
leafgreen = "rom-cache-v9-leafgreen:",
|
|
emerald = "rom-cache-v4-emerald:",
|
|
-- pokeruby/src/string_util.c:408
|
|
ruby = "rom-cache-v6-ruby:",
|
|
sapphire = "rom-cache-v6-sapphire:",
|
|
}
|
|
CacheContract.MARKER_PATH = "rom-cache.complete"
|
|
|
|
CacheContract.REQUIRED_FILES = {
|
|
"data/generated/constants.lua",
|
|
"data/generated/maps.lua",
|
|
"data/generated/text.lua",
|
|
"data/generated/field.lua",
|
|
"data/generated/battle_anims.lua",
|
|
"assets/generated/title/pokemon_logo.png",
|
|
"assets/generated/fonts/font.png",
|
|
"assets/generated/battle/front/pikachu.png",
|
|
"assets/generated/battle/anims/move_anim_0.png",
|
|
"assets/generated/battle/anims/move_anim_1.png",
|
|
"assets/generated/audio/programs.bin",
|
|
"assets/generated/trade/game_boy.png",
|
|
-- engine/items/town_map.asm:296, 150
|
|
"assets/generated/townmap/nest.png",
|
|
"assets/generated/townmap/up_arrow.png",
|
|
}
|
|
|
|
CacheContract.VERSION_REQUIRED_FILES = {
|
|
yellow = {
|
|
"assets/generated/battle/trainers/jessie_james.png",
|
|
"assets/generated/battle/profoakb.png",
|
|
"assets/generated/pikachu/pikapic_1.png",
|
|
"assets/generated/pikachu/pikapic_2.png",
|
|
"assets/generated/pikachu/pikapic_3.png",
|
|
"assets/generated/pikachu/pikapic_4.png",
|
|
"assets/generated/pikachu/pikapic_5.png",
|
|
"assets/generated/pikachu/pikapic_6.png",
|
|
"assets/generated/pikachu/pikapic_7.png",
|
|
"assets/generated/pikachu/pikapic_8.png",
|
|
"assets/generated/pikachu/pikapic_9.png",
|
|
"assets/generated/pikachu/pikapic_10.png",
|
|
"assets/generated/pikachu/pikapic_11.png",
|
|
"assets/generated/pikachu/pikapic_12.png",
|
|
"assets/generated/pikachu/pikapic_13.png",
|
|
"assets/generated/pikachu/pikapic_14.png",
|
|
"assets/generated/pikachu/pikapic_15.png",
|
|
"assets/generated/pikachu/pikapic_16.png",
|
|
"assets/generated/pikachu/pikapic_17.png",
|
|
"assets/generated/pikachu/pikapic_18.png",
|
|
"assets/generated/pikachu/pikapic_19.png",
|
|
"assets/generated/pikachu/pikapic_20.png",
|
|
"assets/generated/pikachu/pikapic_21.png",
|
|
"assets/generated/pikachu/pikapic_22.png",
|
|
"assets/generated/pikachu/pikapic_23.png",
|
|
"assets/generated/pikachu/pikapic_24.png",
|
|
"assets/generated/pikachu/pikapic_25.png",
|
|
"assets/generated/pikachu/pikapic_26.png",
|
|
"assets/generated/pikachu/pikapic_27.png",
|
|
"assets/generated/pikachu/pikapic_28.png",
|
|
"assets/generated/pikachu/gfx_e4841.png",
|
|
"assets/generated/pikachu/gfx_e4ce0.png",
|
|
"assets/generated/pikachu/gfx_e4e70.png",
|
|
"assets/generated/pikachu/gfx_e50af.png",
|
|
"assets/generated/pikachu/gfx_e52fe.png",
|
|
"assets/generated/pikachu/gfx_e5541.png",
|
|
"assets/generated/pikachu/gfx_e5794.png",
|
|
"assets/generated/pikachu/gfx_e59ed.png",
|
|
"assets/generated/pikachu/gfx_e5c4d.png",
|
|
"assets/generated/pikachu/gfx_e5e90.png",
|
|
"assets/generated/pikachu/gfx_e6020.png",
|
|
"assets/generated/pikachu/gfx_e61b0.png",
|
|
"assets/generated/pikachu/gfx_e63f7.png",
|
|
"assets/generated/pikachu/gfx_e6646.png",
|
|
"assets/generated/pikachu/gfx_e682f.png",
|
|
"assets/generated/pikachu/gfx_e69bf.png",
|
|
"assets/generated/pikachu/gfx_e6b4f.png",
|
|
"assets/generated/pikachu/gfx_e6cdf.png",
|
|
"assets/generated/pikachu/gfx_e6e6f.png",
|
|
"assets/generated/pikachu/gfx_e6fff.png",
|
|
"assets/generated/pikachu/gfx_e718f.png",
|
|
"assets/generated/pikachu/gfx_e731f.png",
|
|
"assets/generated/pikachu/gfx_e74af.png",
|
|
"assets/generated/pikachu/gfx_e763f.png",
|
|
"assets/generated/pikachu/gfx_e7863.png",
|
|
"assets/generated/pikachu/gfx_e79f3.png",
|
|
"assets/generated/pikachu/gfx_e7b83.png",
|
|
"assets/generated/pikachu/gfx_e7d13.png",
|
|
"assets/generated/pikachu/gfx_f0b64.png",
|
|
"assets/generated/pikachu/gfx_f0d82.png",
|
|
"assets/generated/minigame/surf_1a.png",
|
|
"assets/generated/minigame/surf_1b.png",
|
|
"assets/generated/minigame/surf_1c.png",
|
|
"assets/generated/minigame/title_bg.png",
|
|
"assets/generated/minigame/intro_pika_0.png",
|
|
},
|
|
}
|
|
|
|
CacheContract.VERSION_REQUIRED_FILES_OVERRIDE = {
|
|
gold = {
|
|
"data/generated/constants.lua",
|
|
"data/generated/maps.lua",
|
|
"data/generated/roofs.lua",
|
|
"data/generated/sprites.lua",
|
|
"data/generated/scripts.lua",
|
|
"data/generated/text.lua",
|
|
-- The engine's label-keyed strings are separate from Gen 2 script text.
|
|
-- Caches made before RomExtractorGen2:extractText must be rebuilt so
|
|
-- src/core/RomText.lua does not silently fall back to built-in wording.
|
|
"data/generated/rom_text.lua",
|
|
"data/generated/pokemon.lua",
|
|
"data/generated/tilesets.lua",
|
|
"data/generated/audio.lua",
|
|
"data/generated/marts.lua",
|
|
"assets/generated/fonts/font.png",
|
|
"assets/generated/fonts/frames.png",
|
|
"assets/generated/title/pokemon_logo.png",
|
|
"assets/generated/title/title_screen.png",
|
|
"assets/generated/title/hooh.png",
|
|
"assets/generated/title/hooh_5.png",
|
|
"assets/generated/title/clouds.png",
|
|
"assets/generated/title/copyright_splash.png",
|
|
"data/generated/oak_speech.lua",
|
|
"assets/generated/intro/oak.png",
|
|
"assets/generated/intro/cal.png",
|
|
"assets/generated/tilesets/johto.png",
|
|
"assets/generated/tilesets/roofs/new_bark.png",
|
|
"assets/generated/sprites/chris.png",
|
|
"assets/generated/battle/front/chikorita.png",
|
|
"assets/generated/battle/front/pikachu.png",
|
|
"assets/generated/battle/front/marill.png",
|
|
"assets/generated/battle/trainers/falkner.png",
|
|
"assets/generated/battle/hud/balls.png",
|
|
"assets/generated/audio/programs.bin",
|
|
"assets/generated/slots/gold_slots_1.png",
|
|
"assets/generated/card_flip/card_flip_1.png",
|
|
"assets/generated/pc/mail_item.png",
|
|
-- engine/events/fishing_gfx.asm:23
|
|
"assets/generated/emotes/fishing.png",
|
|
-- data/sprites/emotes.asm:19, engine/events/field_moves.asm:390
|
|
"assets/generated/emotes/jump_shadow.png",
|
|
"assets/generated/emotes/cut_grass.png",
|
|
-- engine/pokegear/pokegear.asm:2298
|
|
"assets/generated/pokegear/nest_icon.png",
|
|
},
|
|
crystal = {
|
|
"data/generated/constants.lua",
|
|
"data/generated/maps.lua",
|
|
"data/generated/roofs.lua",
|
|
"data/generated/sprites.lua",
|
|
"data/generated/scripts.lua",
|
|
"data/generated/text.lua",
|
|
"data/generated/rom_text.lua",
|
|
"data/generated/events.lua",
|
|
"data/generated/pokemon.lua",
|
|
"data/generated/encounters.lua",
|
|
"data/generated/tilesets.lua",
|
|
"data/generated/landmarks.lua",
|
|
"data/generated/audio.lua",
|
|
"data/generated/marts.lua",
|
|
"data/generated/oak_speech.lua",
|
|
"data/generated/title.lua",
|
|
"data/generated/intro.lua",
|
|
"assets/generated/fonts/font.png",
|
|
"assets/generated/fonts/frames.png",
|
|
-- ../pokecrystal/gfx/font.asm:60
|
|
"assets/generated/fonts/map_entry_sign.png",
|
|
"assets/generated/title/crystal_logo.png",
|
|
"assets/generated/title/crystal_wordmark.png",
|
|
"assets/generated/title/crystal_suicune.png",
|
|
"assets/generated/title/copyright_splash.png",
|
|
"assets/generated/splash/ditto.png",
|
|
"assets/generated/intro/chris.png",
|
|
"assets/generated/intro/kris.png",
|
|
"assets/generated/intro/suicune_run_sprites.png",
|
|
"assets/generated/intro/unowns_tiles.png",
|
|
"assets/generated/intro/oak.png",
|
|
"assets/generated/tilesets/johto.png",
|
|
"assets/generated/tilesets/roofs/new_bark.png",
|
|
"assets/generated/sprites/chris.png",
|
|
"assets/generated/sprites/kris.png",
|
|
"assets/generated/battle/front/chikorita.png",
|
|
"assets/generated/battle/front/wooper.png",
|
|
"assets/generated/battle/front/pikachu.png",
|
|
"assets/generated/battle/trainers/falkner.png",
|
|
"assets/generated/battle/hud/balls.png",
|
|
"assets/generated/audio/programs.bin",
|
|
"assets/generated/slots/gold_slots_1.png",
|
|
"assets/generated/card_flip/card_flip_1.png",
|
|
"assets/generated/pc/mail_item.png",
|
|
"assets/generated/trainer_card/card_f.png",
|
|
"data/generated/mobile_gfx.lua",
|
|
"assets/generated/battle/player_back_female.png",
|
|
"assets/generated/battle/trainers/kris.png",
|
|
"assets/generated/battle/trainers/chris.png",
|
|
-- ../pokecrystal/engine/events/fishing_gfx.asm:38-42
|
|
"assets/generated/emotes/fishing.png",
|
|
-- data/sprites/emotes.asm:19, engine/events/field_moves.asm:390
|
|
"assets/generated/emotes/jump_shadow.png",
|
|
"assets/generated/emotes/cut_grass.png",
|
|
-- engine/pokegear/pokegear.asm:2298
|
|
"assets/generated/pokegear/nest_icon.png",
|
|
},
|
|
firered = {
|
|
"data/generated/gba/help/pack.lua",
|
|
"data/generated/gba/quest_log/pack.lua",
|
|
"data/generated/gba/objects/pack.lua",
|
|
"data/generated/gba/meta.json",
|
|
"data/generated/gba/maps.json",
|
|
"data/generated/gba/audio/meta.json",
|
|
"data/generated/intro.lua",
|
|
"data/generated/gba/intro/meta.json",
|
|
"data/generated/gba/intro/oak.png",
|
|
"data/generated/gba/intro/boy.png",
|
|
"data/generated/gba/intro/girl.png",
|
|
"data/generated/gba/intro/rival.png",
|
|
"data/generated/gba/intro/title_screen.png",
|
|
"data/generated/gba/intro/title_logo.png",
|
|
"data/generated/gba/intro/box_art_mon.png",
|
|
"data/generated/gba/intro/press_start.png",
|
|
"data/generated/gba/intro/platform.png",
|
|
"data/generated/gba/intro/oak_speech_bg.png",
|
|
"data/generated/gba/intro/controls_page1.png",
|
|
"data/generated/gba/intro/pikachu_intro_bg.png",
|
|
"data/generated/gba/intro/nidoran_f.png",
|
|
"data/generated/gba/naming/manifest.lua",
|
|
"data/generated/gba/ow/manifest.lua",
|
|
"data/generated/gba/ow/palette_manifest.lua",
|
|
"data/generated/gba/ow/0.rgba",
|
|
"data/generated/gba/ow/7.rgba",
|
|
"data/generated/gba/pokemon/manifest.lua",
|
|
"data/generated/gba/pokemon/names.lua",
|
|
"data/generated/gba/pokemon/front/1.rgba",
|
|
"data/generated/gba/pokemon/front/200.rgba",
|
|
"data/generated/gba/pokemon/front/411.rgba",
|
|
"data/generated/gba/pokemon/back/1.rgba",
|
|
"data/generated/gba/pokemon/back/200.rgba",
|
|
"data/generated/gba/pokemon/back/411.rgba",
|
|
"data/generated/gba/pokemon/icons/1.rgba",
|
|
"data/generated/gba/pokemon/icons/200.rgba",
|
|
"data/generated/gba/pokemon/icons/411.rgba",
|
|
-- src/pokedex_screen.c:930
|
|
"data/generated/gba/pokemon/pokedex/paper_bg.rgba",
|
|
-- src/pokedex_screen.c:2901
|
|
"data/generated/gba/pokemon/pokedex/footprints/1.rgba",
|
|
"data/generated/gba/pokemon/pokedex/footprints/bulbasaur.rgba",
|
|
"data/generated/gba/pokemon/pokedex/footprints/question_mark.rgba",
|
|
"data/generated/gba/region_map/kanto_map.png",
|
|
"data/generated/gba/region_map/cursor.png",
|
|
"data/generated/gba/region_map/player_red.png",
|
|
-- src/region_map.c:425
|
|
"data/generated/gba/region_map/fly_icon.rgba",
|
|
"data/generated/gba/region_map/fly_icon.png",
|
|
"data/generated/gba/region_map/map_sections.lua",
|
|
-- src/heal_location.c:62, src/region_map.c:4023
|
|
"data/generated/gba/region_map/heal_locations.lua",
|
|
"data/generated/gba/region_map/fly_destinations.lua",
|
|
"data/generated/gba/scripts/multichoice.lua",
|
|
"data/generated/gba/pokemon/stats.lua",
|
|
"data/generated/gba/pokemon/learnsets.lua",
|
|
"data/generated/gba/pokemon/move_names.lua",
|
|
"data/generated/gba/pokemon/battle_moves.lua",
|
|
"data/generated/gba/pokemon/battle/manifest.lua",
|
|
"data/generated/gba/pokemon/battle/healthbox_player.rgba",
|
|
"data/generated/gba/pokemon/battle/healthbox_doubles_player.rgba",
|
|
"data/generated/gba/pokemon/battle/healthbox_doubles_opponent.rgba",
|
|
"data/generated/gba/pokemon/battle/hp_bold_digits.rgba",
|
|
"data/generated/gba/pokemon/battle/terrain_building.rgba",
|
|
-- src/battle_bg.c:439
|
|
"data/generated/gba/pokemon/battle/terrain_grass.rgba",
|
|
"data/generated/gba/pokemon/battle/terrain_cave.rgba",
|
|
"data/generated/gba/pokemon/battle/terrain_water.rgba",
|
|
"data/generated/gba/pokemon/battle/terrain_champion.rgba",
|
|
"data/generated/gba/pokemon/battle/terrain_bg_cave.rgba",
|
|
"data/generated/gba/pokemon/battle/ball_open/manifest.lua",
|
|
"data/generated/gba/pokemon/battle/ball_open/particles.rgba",
|
|
"data/generated/gba/pokemon/battle/ball_open/balls.rgba",
|
|
"data/generated/gba/pokemon/battle_transition/manifest.lua",
|
|
"data/generated/gba/pokemon/battle_transition/big_pokeball.rgba",
|
|
"data/generated/gba/pokemon/battle_transition/sliding_pokeball.rgba",
|
|
"data/generated/gba/pokemon/party/slot_main.rgba",
|
|
-- src/party_menu.c:2273
|
|
"data/generated/gba/pokemon/party/slot_main_multi.rgba",
|
|
"data/generated/gba/pokemon/party/slot_main_multi_selected.rgba",
|
|
"data/generated/gba/pokemon/party/slot_wide_multi.rgba",
|
|
"data/generated/gba/pokemon/party/slot_wide_multi_selected.rgba",
|
|
-- src/data/party_menu.h:664
|
|
"data/generated/gba/pokemon/party/hold_icons.rgba",
|
|
"data/generated/gba/items/bag/manifest.lua",
|
|
"data/generated/gba/items/bag/bg.rgba",
|
|
"data/generated/gba/items/bag/bg_female.rgba",
|
|
"data/generated/gba/items/bag/list.rgba",
|
|
"data/generated/gba/items/bag/list_female.rgba",
|
|
"data/generated/gba/items/bag/list_blank.rgba",
|
|
"data/generated/gba/items/bag/list_blank_female.rgba",
|
|
"data/generated/gba/items/bag/desc_sel.rgba",
|
|
"data/generated/gba/items/bag/red_arrow.rgba",
|
|
-- src/item_menu.c:569, src/item_menu_icons.c:150
|
|
"data/generated/gba/items/bag/bg_itempc.rgba",
|
|
"data/generated/gba/items/bag/bg_itempc_female.rgba",
|
|
"data/generated/gba/items/bag/swap_line.rgba",
|
|
-- src/item_pc.c:435
|
|
"data/generated/gba/items/item_pc/bg.rgba",
|
|
"data/generated/gba/items/item_pc/bg_submenu.rgba",
|
|
-- src/pokedex_screen.c:1161
|
|
"data/generated/gba/pokemon/pokedex/chrome.lua",
|
|
"data/generated/gba/items/shop/manifest.lua",
|
|
"data/generated/gba/items/shop/bg.rgba",
|
|
"data/generated/gba/doors/manifest.lua",
|
|
"data/generated/gba/doors/pallet.rgba",
|
|
"data/generated/gba/native/manifest.lua",
|
|
-- src/scrcmd.c:711, include/constants/layouts.h:253,267,268,308
|
|
"data/generated/gba/native/layouts/alt_264.mid",
|
|
"data/generated/gba/native/layouts/alt_278.mid",
|
|
"data/generated/gba/native/layouts/alt_279.mid",
|
|
"data/generated/gba/native/layouts/alt_319.mid",
|
|
"data/generated/gba/pokemon/summary/manifest.lua",
|
|
"data/generated/gba/pokemon/summary/menu_info.rgba",
|
|
-- src/pokemon_summary_screen.c:1862
|
|
"data/generated/gba/pokemon/summary/bg3_info.rgba",
|
|
"data/generated/gba/pokemon/summary/bg3_moves.rgba",
|
|
"data/generated/gba/pokemon/summary/layer_moves_info.rgba",
|
|
"data/generated/gba/pokemon/summary/progress_moves_info_select.rgba",
|
|
"data/generated/gba/pokemon/summary/markings.rgba",
|
|
"data/generated/gba/pokemon/storage/manifest.lua",
|
|
"data/generated/gba/pokedex/manifest.lua",
|
|
"data/generated/gba/chrome/manifest.lua",
|
|
"data/generated/gba/chrome/menu_message_rgba.rgba",
|
|
"data/generated/gba/chrome/std_rgba.rgba",
|
|
"data/generated/gba/chrome/signpost_rgba.rgba",
|
|
"data/generated/gba/chrome/user_frame_0.rgba",
|
|
"data/generated/gba/chrome/user_frame_9.rgba",
|
|
"data/generated/gba/chrome/fonts/latin_normal_fg.rgba",
|
|
"data/generated/gba/chrome/fonts/latin_widths.lua",
|
|
-- src/text.c:100
|
|
"data/generated/gba/chrome/fonts/latin_small_fg.rgba",
|
|
"data/generated/gba/chrome/fonts/latin_small_shadow.rgba",
|
|
"data/generated/gba/chrome/fonts/latin_small_widths.lua",
|
|
-- src/text.c:141, :227, :228 (the Japanese fonts)
|
|
"data/generated/gba/chrome/fonts/japanese_normal_fg.rgba",
|
|
"data/generated/gba/chrome/fonts/japanese_normal_shadow.rgba",
|
|
"data/generated/gba/chrome/fonts/japanese_widths.lua",
|
|
"data/generated/gba/chrome/fonts/japanese_small_fg.rgba",
|
|
"data/generated/gba/chrome/fonts/japanese_small_shadow.rgba",
|
|
-- src/braille_text.c:15
|
|
"data/generated/gba/chrome/fonts/braille_fg.rgba",
|
|
"data/generated/gba/chrome/fonts/braille_shadow.rgba",
|
|
"data/generated/gba/chrome/fonts/braille.lua",
|
|
-- src/seagallop.c:41
|
|
"data/generated/gba/seagallop/manifest.lua",
|
|
"data/generated/gba/seagallop/water.4bpp",
|
|
"data/generated/gba/seagallop/ferry.4bpp",
|
|
"data/generated/gba/seagallop/wake.4bpp",
|
|
"data/generated/gba/seagallop/wb_tilemap.bin",
|
|
"data/generated/gba/seagallop/eb_tilemap.bin",
|
|
"data/generated/gba/seagallop/wb.rgba",
|
|
"data/generated/gba/seagallop/eb.rgba",
|
|
"data/generated/gba/trainers.lua",
|
|
"data/generated/gba/trainers/back_0.rgba",
|
|
"data/generated/gba/trainers/back_1.rgba",
|
|
"data/generated/gba/trainer_card/manifest.lua",
|
|
"data/generated/gba/trainer_card/bg.rgba",
|
|
"data/generated/gba/field_effects/tall_grass.rgba",
|
|
"data/generated/gba/field_effects/cut_grass.rgba",
|
|
"data/generated/gba/field_effects/rock_smash.rgba",
|
|
"data/generated/gba/field_effects/surf_blob.rgba",
|
|
"data/generated/gba/field_effects/fly_bird.rgba",
|
|
"data/generated/gba/field_effects/ripple.rgba",
|
|
-- src/field_effect.c:3963 (Birth Island meteorite shatter shards)
|
|
"data/generated/gba/field_effects/deoxys_rock_fragments.rgba",
|
|
-- src/data/field_effects/field_effect_objects.h:565,1203
|
|
"data/generated/gba/field_effects/splash.rgba",
|
|
"data/generated/gba/field_effects/hot_springs_water.rgba",
|
|
"data/generated/gba/field_effects/emoticons.rgba",
|
|
-- src/slot_machine.c:399, :739
|
|
"data/generated/gba/slot_machine/manifest.lua",
|
|
"data/generated/gba/slot_machine/reel_icons.rgba",
|
|
"data/generated/gba/slot_machine/clefairy.rgba",
|
|
"data/generated/gba/slot_machine/digits.rgba",
|
|
"data/generated/gba/slot_machine/bg.rgba",
|
|
"data/generated/gba/slot_machine/payout_lights.rgba",
|
|
"data/generated/gba/slot_machine/match_lines.rgba",
|
|
"data/generated/gba/slot_machine/button_pressed.rgba",
|
|
"data/generated/gba/slot_machine/combos_window.rgba",
|
|
-- src/trade_scene.c:151
|
|
"data/generated/gba/trade/manifest.lua",
|
|
"data/generated/gba/trade/gba_screen.rgba",
|
|
"data/generated/gba/trade/gba_screen_wireless.rgba",
|
|
"data/generated/gba/trade/gba_screen_flash.rgba",
|
|
"data/generated/gba/trade/cable_closeup.rgba",
|
|
"data/generated/gba/trade/cable_end.rgba",
|
|
"data/generated/gba/trade/link_mon_glow.rgba",
|
|
"data/generated/gba/trade/link_mon_shadow.rgba",
|
|
-- src/trade_scene.c:1121
|
|
"data/generated/gba/trade/mon_shadow_bg.rgba",
|
|
"data/generated/gba/trade/ball.rgba",
|
|
"data/generated/gba/trade/ball_spin.rgba",
|
|
-- src/trade.c:1368
|
|
"data/generated/gba/trade/menu_bg1.rgba",
|
|
"data/generated/gba/trade/stripes_bg2.rgba",
|
|
"data/generated/gba/trade/stripes_bg3.rgba",
|
|
"data/generated/gba/trade/party_box.rgba",
|
|
"data/generated/gba/trade/moves_box.rgba",
|
|
"data/generated/gba/trade/mon_box.rgba",
|
|
"data/generated/gba/trade/menu_tiles.rgba",
|
|
"data/generated/gba/trade/cursor.rgba",
|
|
-- src/link_rfu_3.c:34, src/union_room_chat_objects.c:30
|
|
"data/generated/gba/union_room/manifest.lua",
|
|
"data/generated/gba/union_room/wireless_icon.rgba",
|
|
"data/generated/gba/union_room/chat_bg.rgba",
|
|
"data/generated/gba/union_room/chat_panel.rgba",
|
|
"data/generated/gba/union_room/chat_icons.rgba",
|
|
"data/generated/gba/union_room/chat_selector_cursor.rgba",
|
|
"data/generated/gba/union_room/chat_text_entry_cursor.rgba",
|
|
"data/generated/gba/union_room/chat_char_select_cursor.rgba",
|
|
"data/generated/gba/union_room/chat_r_button.rgba",
|
|
-- src/wireless_communication_status_screen.c:50
|
|
"data/generated/gba/wireless_status/manifest.lua",
|
|
"data/generated/gba/wireless_status/bg.rgba",
|
|
"data/generated/gba/wireless_status/bg_index.bin",
|
|
"data/generated/gba/wireless_status/palettes.pal",
|
|
-- src/fame_checker.c:119, src/graphics.c:1230
|
|
"data/generated/gba/fame_checker/manifest.lua",
|
|
"data/generated/gba/fame_checker/bg.rgba",
|
|
"data/generated/gba/fame_checker/pick_panel.rgba",
|
|
"data/generated/gba/fame_checker/0.rgba",
|
|
"data/generated/gba/fame_checker/1.rgba",
|
|
"data/generated/gba/fame_checker/13.rgba",
|
|
"data/generated/gba/fame_checker/14.rgba",
|
|
"data/generated/gba/fame_checker/cursor.rgba",
|
|
"data/generated/gba/fame_checker/question_mark.rgba",
|
|
"data/generated/gba/fame_checker/silhouette.pal",
|
|
"data/generated/gba/fame_checker/pack.lua",
|
|
-- src/graphics.c:1117, src/teachy_tv.c:526
|
|
"data/generated/gba/teachy_tv/manifest.lua",
|
|
"data/generated/gba/teachy_tv/screen.rgba",
|
|
"data/generated/gba/teachy_tv/title.rgba",
|
|
"data/generated/gba/teachy_tv/end.rgba",
|
|
-- src/teachy_tv.c:637, :1218
|
|
"data/generated/gba/teachy_tv/static.rgba",
|
|
"data/generated/gba/teachy_tv/bg3.rgba",
|
|
-- src/mystery_gift_show_card.c:150, src/mystery_gift_show_news.c:99
|
|
"data/generated/gba/mystery_gift/manifest.lua",
|
|
"data/generated/gba/mystery_gift/card_bg0.rgba",
|
|
"data/generated/gba/mystery_gift/card_bg7.rgba",
|
|
"data/generated/gba/mystery_gift/news_bg0.rgba",
|
|
"data/generated/gba/mystery_gift/news_bg7.rgba",
|
|
-- src/trainer_tower_sets.c:8956
|
|
"data/generated/gba/trainer_tower.lua",
|
|
-- src/data/pokemon/tutor_learnsets.h:22
|
|
"data/generated/gba/pokemon/tutor.lua",
|
|
-- src/trainer_tower.c:554, include/constants/layouts.h:355, :363
|
|
"data/generated/gba/native/layouts/alt_366.mid",
|
|
"data/generated/gba/native/layouts/alt_373.mid",
|
|
"data/generated/gba/native/layouts/alt_374.mid",
|
|
"data/generated/gba/native/layouts/alt_381.mid",
|
|
-- src/script_menu.c:1161
|
|
"data/generated/gba/museum/manifest.lua",
|
|
"data/generated/gba/museum/kabutops.rgba",
|
|
"data/generated/gba/museum/aerodactyl.rgba",
|
|
-- src/region_map.c:790 sAnim_DungeonIconVisited
|
|
"data/generated/gba/region_map/dungeon_icon_visited.rgba",
|
|
"data/generated/gba/region_map/dungeon_icon_visited.png",
|
|
-- src/learn_move.c:403
|
|
"data/generated/gba/move_relearner/manifest.lua",
|
|
"data/generated/gba/move_relearner/bg.rgba",
|
|
-- src/daycare.c:137, :138, :139
|
|
"data/generated/gba/pokemon/egg/manifest.lua",
|
|
"data/generated/gba/pokemon/egg/hatch.rgba",
|
|
"data/generated/gba/pokemon/egg/shard.rgba",
|
|
"data/generated/gba/pokemon/front/412.rgba",
|
|
-- src/party_menu.c:2655
|
|
"data/generated/gba/pokemon/icons/412.rgba",
|
|
-- src/battle_records.c:563
|
|
"data/generated/gba/trainer_tower/manifest.lua",
|
|
"data/generated/gba/trainer_tower/records_bg.rgba",
|
|
-- src/trainer_card.c:265, :1454, :1560
|
|
"data/generated/gba/trainer_card/front_0.rgba",
|
|
"data/generated/gba/trainer_card/front_4_female.rgba",
|
|
"data/generated/gba/trainer_card/back_0.rgba",
|
|
"data/generated/gba/trainer_card/back_4_female.rgba",
|
|
"data/generated/gba/trainer_card/screen_0.rgba",
|
|
"data/generated/gba/trainer_card/screen_4_female.rgba",
|
|
"data/generated/gba/trainer_card/star.rgba",
|
|
"data/generated/gba/trainer_card/stickers.rgba",
|
|
-- src/credits.c:815, :1101, :1230
|
|
"data/generated/gba/credits/manifest.lua",
|
|
"data/generated/gba/credits/pack.lua",
|
|
"data/generated/gba/credits/copyright.rgba",
|
|
"data/generated/gba/credits/the_end.rgba",
|
|
"data/generated/gba/credits/circle.rgba",
|
|
"data/generated/gba/credits/pokeball_0.rgba",
|
|
"data/generated/gba/credits/mon_0_1.rgba",
|
|
"data/generated/gba/credits/player_male.rgba",
|
|
"data/generated/gba/credits/rival.rgba",
|
|
"data/generated/gba/credits/ground_grass.rgba",
|
|
-- src/field_specials.c:2133
|
|
"data/generated/gba/league/lighting.lua",
|
|
-- src/diploma.c:119
|
|
"data/generated/gba/diploma/manifest.lua",
|
|
"data/generated/gba/diploma/kanto.rgba",
|
|
"data/generated/gba/diploma/national.rgba",
|
|
-- src/hall_of_fame.c:1163, :1181
|
|
"data/generated/gba/hall_of_fame/manifest.lua",
|
|
"data/generated/gba/hall_of_fame/bands.rgba",
|
|
"data/generated/gba/hall_of_fame/stripes.rgba",
|
|
"data/generated/gba/hall_of_fame/confetti.rgba",
|
|
-- src/data/field_effects/field_effect_objects.h:288, src/itemfinder.c:39
|
|
"data/generated/gba/field_effects/ground_impact_dust.rgba",
|
|
"data/generated/gba/field_effects/itemfinder_arrow_star.rgba",
|
|
"data/generated/gba/field_effects/arrow.rgba",
|
|
-- src/field_effect.c:73, :77
|
|
"data/generated/gba/field_effects/field_move_streaks_outdoors.rgba",
|
|
"data/generated/gba/field_effects/field_move_streaks_indoors.rgba",
|
|
-- src/pokemon.c:5904, src/decompress.c:83
|
|
"data/generated/gba/pokemon/front_shiny/1.rgba",
|
|
"data/generated/gba/pokemon/back_shiny/1.rgba",
|
|
"data/generated/gba/pokemon/front_shiny/385_3.rgba",
|
|
"data/generated/gba/pokemon/front/413.rgba",
|
|
"data/generated/gba/pokemon/back/439.rgba",
|
|
"data/generated/gba/pokemon/front_shiny/439.rgba",
|
|
"data/generated/gba/pokemon/back_shiny/439.rgba",
|
|
"data/generated/gba/pokemon/icons/439.rgba",
|
|
-- src/pokemon.c:1350, :5339
|
|
"data/generated/gba/pokemon/spinda/front.4bpp",
|
|
"data/generated/gba/pokemon/spinda/normal.gbapal",
|
|
"data/generated/gba/pokemon/spinda/shiny.gbapal",
|
|
"data/generated/gba/pokemon/spinda/spots.bin",
|
|
-- src/ss_anne.c:21-22
|
|
"data/generated/gba/field_effects/ss_anne_wake.rgba",
|
|
"data/generated/gba/field_effects/ss_anne_smoke.rgba",
|
|
-- src/data/trainer_graphics/front_pic_tables.h:153
|
|
"data/generated/gba/trainers/front/0.rgba",
|
|
"data/generated/gba/trainers/front/147.rgba",
|
|
-- src/pokeball.c:61
|
|
"data/generated/gba/intro/ball_poke.png",
|
|
-- src/battle_message.c:517, src/item_menu.c:183, src/oak_speech.c:588
|
|
"data/generated/gba/scripts/text_tables.lua",
|
|
-- src/easy_chat.c:41, src/data/easy_chat/easy_chat_groups.h:26
|
|
"data/generated/gba/easy_chat/words.lua",
|
|
-- src/fldeff_flash.c:157-162
|
|
"data/generated/gba/cave_transition/screen.bin",
|
|
"data/generated/gba/cave_transition/palettes.lua",
|
|
-- src/data/ingame_trades.h:1, :184
|
|
"data/generated/gba/trades/ingame_trades.lua",
|
|
-- src/pokemon.c:1666, :6206
|
|
"data/generated/gba/trainers/union_room_classes.lua",
|
|
-- src/region_map.c:393-427, :527, :3158-3171, :3359
|
|
"data/generated/gba/region_map/manifest.lua",
|
|
"data/generated/gba/region_map/layouts.lua",
|
|
"data/generated/gba/region_map/section_geometry.lua",
|
|
"data/generated/gba/region_map/sevii123_map.png",
|
|
"data/generated/gba/region_map/sevii45_map.png",
|
|
"data/generated/gba/region_map/sevii67_map.png",
|
|
"data/generated/gba/region_map/switch_button.png",
|
|
"data/generated/gba/region_map/navel_rock_patch.png",
|
|
"data/generated/gba/region_map/birth_island_patch.png",
|
|
"data/generated/gba/region_map/frame_normal.png",
|
|
"data/generated/gba/region_map/frame_fly.png",
|
|
"data/generated/gba/region_map/switch_menu_123.png",
|
|
"data/generated/gba/region_map/switch_menu_all.png",
|
|
"data/generated/gba/region_map/switch_cursor_left.png",
|
|
"data/generated/gba/region_map/switch_cursor_right.png",
|
|
"data/generated/gba/region_map/edge_top_left.png",
|
|
"data/generated/gba/region_map/edge_top_right.png",
|
|
"data/generated/gba/region_map/edge_mid_left.png",
|
|
"data/generated/gba/region_map/edge_mid_right.png",
|
|
"data/generated/gba/region_map/edge_bottom_left.png",
|
|
"data/generated/gba/region_map/edge_bottom_right.png",
|
|
-- data/battle_ai_scripts.s:17
|
|
"data/generated/gba/battle_ai/pack.lua",
|
|
-- src/menu_indicators.c:258
|
|
"data/generated/gba/chrome/scroll_arrows.rgba",
|
|
"data/generated/gba/chrome/red_arrow_cursor.rgba",
|
|
"data/generated/gba/chrome/selector_outline.rgba",
|
|
"data/generated/gba/chrome/red_arrow.pal",
|
|
-- src/minigame_countdown.c:213, src/pokemon_jump.c:454, src/digit_obj_util.c:67
|
|
"data/generated/gba/link/countdown_321.pal",
|
|
"data/generated/gba/link/countdown_321.rgba",
|
|
"data/generated/gba/link/manifest.lua",
|
|
"data/generated/gba/link/minigame_countdown.pal",
|
|
"data/generated/gba/link/minigame_countdown_numbers.rgba",
|
|
"data/generated/gba/link/minigame_countdown_start.rgba",
|
|
"data/generated/gba/link/minigame_digits.pal",
|
|
"data/generated/gba/link/minigame_digits.rgba",
|
|
-- src/mystery_gift_show_card.c:115, src/mystery_gift_menu.c:33
|
|
"data/generated/gba/mystery_gift/border.pal",
|
|
"data/generated/gba/mystery_gift/border_tiles.rgba",
|
|
"data/generated/gba/mystery_gift/menu_bg.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow.pal",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_0.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_1.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_2.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_3.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_4.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_5.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_6.rgba",
|
|
"data/generated/gba/mystery_gift/stamp_shadow_7.rgba",
|
|
-- src/union_room_player_avatar.c:33
|
|
"data/generated/gba/union_room/avatars.lua",
|
|
-- src/berry_crush.c:647, src/graphics.c:1378
|
|
"data/generated/gba/berry_crush/bg.bin",
|
|
"data/generated/gba/berry_crush/bg.rgba",
|
|
"data/generated/gba/berry_crush/container_cap.bin",
|
|
"data/generated/gba/berry_crush/container_cap.rgba",
|
|
"data/generated/gba/berry_crush/core.pal",
|
|
"data/generated/gba/berry_crush/crusher.4bpp",
|
|
"data/generated/gba/berry_crush/crusher.pal",
|
|
"data/generated/gba/berry_crush/crusher.rgba",
|
|
"data/generated/gba/berry_crush/crusher_base.rgba",
|
|
"data/generated/gba/berry_crush/crusher_top.bin",
|
|
"data/generated/gba/berry_crush/crusher_top.rgba",
|
|
"data/generated/gba/berry_crush/effect.pal",
|
|
"data/generated/gba/berry_crush/impact.rgba",
|
|
"data/generated/gba/berry_crush/manifest.lua",
|
|
"data/generated/gba/berry_crush/sparkle.rgba",
|
|
"data/generated/gba/berry_crush/tables.lua",
|
|
"data/generated/gba/berry_crush/text_windows.bin",
|
|
"data/generated/gba/berry_crush/text_windows.rgba",
|
|
"data/generated/gba/berry_crush/timer.pal",
|
|
"data/generated/gba/berry_crush/timer_digits.rgba",
|
|
-- src/dodrio_berry_picking.c:3323
|
|
"data/generated/gba/dodrio_berry_picking/berries.pal",
|
|
"data/generated/gba/dodrio_berry_picking/berries.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/bg.bin",
|
|
"data/generated/gba/dodrio_berry_picking/bg.pal",
|
|
"data/generated/gba/dodrio_berry_picking/bg.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/cloud.pal",
|
|
"data/generated/gba/dodrio_berry_picking/cloud.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/dodrio.pal",
|
|
"data/generated/gba/dodrio_berry_picking/dodrio.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/dodrio_shiny.pal",
|
|
"data/generated/gba/dodrio_berry_picking/dodrio_shiny.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/manifest.lua",
|
|
"data/generated/gba/dodrio_berry_picking/scenery.4bpp",
|
|
"data/generated/gba/dodrio_berry_picking/status.pal",
|
|
"data/generated/gba/dodrio_berry_picking/status.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/tables.lua",
|
|
"data/generated/gba/dodrio_berry_picking/tree_border.4bpp",
|
|
"data/generated/gba/dodrio_berry_picking/tree_border_left.bin",
|
|
"data/generated/gba/dodrio_berry_picking/tree_border_left.rgba",
|
|
"data/generated/gba/dodrio_berry_picking/tree_border_right.bin",
|
|
"data/generated/gba/dodrio_berry_picking/tree_border_right.rgba",
|
|
-- src/pokemon_jump.c:2901
|
|
"data/generated/gba/pokemon_jump/bg.4bpp",
|
|
"data/generated/gba/pokemon_jump/bg.bin",
|
|
"data/generated/gba/pokemon_jump/bg.pal",
|
|
"data/generated/gba/pokemon_jump/bg.rgba",
|
|
"data/generated/gba/pokemon_jump/bonuses.4bpp",
|
|
"data/generated/gba/pokemon_jump/bonuses.bin",
|
|
"data/generated/gba/pokemon_jump/bonuses.rgba",
|
|
"data/generated/gba/pokemon_jump/manifest.lua",
|
|
"data/generated/gba/pokemon_jump/pal1.pal",
|
|
"data/generated/gba/pokemon_jump/pal2.pal",
|
|
"data/generated/gba/pokemon_jump/star.rgba",
|
|
"data/generated/gba/pokemon_jump/tables.lua",
|
|
"data/generated/gba/pokemon_jump/venusaur.4bpp",
|
|
"data/generated/gba/pokemon_jump/venusaur.bin",
|
|
"data/generated/gba/pokemon_jump/venusaur.rgba",
|
|
"data/generated/gba/pokemon_jump/vine1.rgba",
|
|
"data/generated/gba/pokemon_jump/vine1_pal2.rgba",
|
|
"data/generated/gba/pokemon_jump/vine2.rgba",
|
|
"data/generated/gba/pokemon_jump/vine2_pal2.rgba",
|
|
"data/generated/gba/pokemon_jump/vine3.rgba",
|
|
"data/generated/gba/pokemon_jump/vine3_pal2.rgba",
|
|
"data/generated/gba/pokemon_jump/vine4.rgba",
|
|
"data/generated/gba/pokemon_jump/vine4_pal2.rgba",
|
|
},
|
|
}
|
|
do
|
|
local frlg = CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.firered
|
|
for _, path in ipairs(require("src.import.gba.battle_anim_extract").FRLG_REQUIRED) do
|
|
frlg[#frlg + 1] = "data/generated/gba/" .. path
|
|
end
|
|
-- src/trainer_card.c:155, :293
|
|
local hoenn = "data/generated/gba/rse/trainer_card/"
|
|
frlg[#frlg + 1] = hoenn .. "manifest.lua"
|
|
frlg[#frlg + 1] = hoenn .. "badges.png"
|
|
frlg[#frlg + 1] = hoenn .. "star.png"
|
|
for stars = 0, 4 do
|
|
for _, side in ipairs({ "screen", "front", "back" }) do
|
|
frlg[#frlg + 1] = string.format("%s%s_%d.png", hoenn, side, stars)
|
|
frlg[#frlg + 1] = string.format("%s%s_%d_female.png", hoenn, side, stars)
|
|
end
|
|
end
|
|
-- pokefirered/src/battle_script_commands.c:9693
|
|
local terrainKeys = require("src.import.gba.battle_chrome_extract").TERRAIN_KEYS
|
|
for id = 0, 19 do
|
|
frlg[#frlg + 1] = "data/generated/gba/pokemon/battle/terrain_" .. terrainKeys[id] .. "_post_dex.rgba"
|
|
end
|
|
end
|
|
CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.leafgreen = {}
|
|
for i, path in ipairs(CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.firered) do
|
|
CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.leafgreen[i] = path
|
|
end
|
|
table.insert(CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.leafgreen,
|
|
"data/generated/gba/intro/title_streak.png")
|
|
CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.silver =
|
|
CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.gold
|
|
|
|
local SEMANTIC_MODULES = {
|
|
[1] = {
|
|
"constants", "maps", "tilesets", "text", "text_pointers",
|
|
"trainer_headers", "font", "sprites", "pokemon", "moves", "items",
|
|
"type_chart", "trainers", "encounters", "field", "battle_anims",
|
|
},
|
|
[2] = {
|
|
"pokemon", "moves", "items", "type_chart", "audio", "font", "maps",
|
|
"tilesets", "text", "rom_text", "trainers", "encounters", "sprites",
|
|
"palettes", "icons", "battle_anims", "constants", "landmarks",
|
|
},
|
|
[3] = { "maps", "intro", "audio" },
|
|
}
|
|
|
|
local OPTIONAL_SEMANTIC_MODULES = {
|
|
[1] = { "audio", "palettes", "icons" },
|
|
[2] = {},
|
|
[3] = {},
|
|
}
|
|
|
|
local function copy(values)
|
|
local out = {}
|
|
for index, value in ipairs(values or {}) do out[index] = value end
|
|
return out
|
|
end
|
|
|
|
CacheContract.PLAN_CORE_FILES = {
|
|
"data/generated/gba/meta.json",
|
|
"data/generated/gba/maps.json",
|
|
"data/generated/gba/audio/meta.json",
|
|
"data/generated/gba/intro/meta.json",
|
|
"data/generated/maps.lua",
|
|
"data/generated/intro.lua",
|
|
"data/generated/audio.lua",
|
|
}
|
|
|
|
local composed = {}
|
|
|
|
function CacheContract.planFilesFor(version)
|
|
if composed[version] then return composed[version] end
|
|
local Plans = require("src.import.gba.plans.registry")
|
|
local CachePaths = require("src.core.game3.cache_paths")
|
|
local files, seen = {}, {}
|
|
local function add(path)
|
|
if not seen[path] then
|
|
seen[path] = true
|
|
files[#files + 1] = path
|
|
end
|
|
end
|
|
for _, path in ipairs(CacheContract.PLAN_CORE_FILES) do add(path) end
|
|
for _, path in ipairs(Plans.required(Plans.of(version), CachePaths.CACHE_ROOT)) do add(path) end
|
|
if version == "emerald" then
|
|
local cfg = require("src.import.gba.games.emerald").BATTLE_UI
|
|
local keys = require("src.import.gba.battle_chrome_extract").TERRAIN_KEYS
|
|
-- pokeemerald/src/battle_script_commands.c:10133
|
|
local function postDex(key)
|
|
add(CachePaths.CACHE_ROOT .. "/pokemon/battle/terrain_" .. key .. "_post_dex.rgba")
|
|
end
|
|
for id = 0, cfg.terrain_count - 1 do postDex(keys[id]) end
|
|
for _, scene in ipairs(cfg.scenes) do postDex(scene.key) end
|
|
end
|
|
composed[version] = files
|
|
return files
|
|
end
|
|
|
|
local function planComposed(version)
|
|
return CacheContract.VERSION_REQUIRED_FILES_OVERRIDE[version] == nil
|
|
and CacheContract.VERSION_FORMAT[version] ~= nil
|
|
and GameVersion.VERSIONS[version] ~= nil
|
|
and GameVersion.generation(version) == 3
|
|
and GameVersion.layout(version) ~= nil
|
|
end
|
|
|
|
function CacheContract.requiredFilesFor(version)
|
|
local override = CacheContract.VERSION_REQUIRED_FILES_OVERRIDE[version]
|
|
if override then return override, true end
|
|
if planComposed(version) then return CacheContract.planFilesFor(version), true end
|
|
return CacheContract.REQUIRED_FILES, false
|
|
end
|
|
|
|
-- A detached, version-specific inventory for read-only consumers. Semantic
|
|
-- consumers additionally require every generated module that backs the public
|
|
-- registry surface; optional semantic roots are discovered lazily.
|
|
function CacheContract.requiredFiles(version, semantic)
|
|
local base, isOverride = CacheContract.requiredFilesFor(version)
|
|
local files = copy(base)
|
|
if not isOverride then
|
|
for _, path in ipairs(CacheContract.VERSION_REQUIRED_FILES[version] or {}) do
|
|
files[#files + 1] = path
|
|
end
|
|
end
|
|
if semantic then
|
|
for _, name in ipairs(SEMANTIC_MODULES[GameVersion.generation(version)] or {}) do
|
|
files[#files + 1] = "data/generated/" .. name .. ".lua"
|
|
end
|
|
end
|
|
local seen, out = {}, {}
|
|
for _, path in ipairs(files) do
|
|
if not seen[path] then
|
|
seen[path] = true
|
|
out[#out + 1] = path
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
function CacheContract.semanticModules(version)
|
|
return copy(SEMANTIC_MODULES[GameVersion.generation(version)])
|
|
end
|
|
|
|
function CacheContract.optionalSemanticModules(version)
|
|
return copy(OPTIONAL_SEMANTIC_MODULES[GameVersion.generation(version)])
|
|
end
|
|
|
|
function CacheContract.formatFor(version)
|
|
return CacheContract.VERSION_FORMAT[version] or CacheContract.FORMAT
|
|
end
|
|
|
|
function CacheContract.markerFor(version, sha1)
|
|
return CacheContract.formatFor(version)
|
|
.. (sha1 or GameVersion.info(version).sha1)
|
|
end
|
|
|
|
function CacheContract.markerMatches(version, marker)
|
|
for _, revision in ipairs(GameVersion.revisions(version)) do
|
|
if marker == CacheContract.markerFor(version, revision.sha1) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Keep the process-global CacheFs prefix isolated even when a filesystem
|
|
-- adapter raises while probing or publishing. The real CacheFs methods
|
|
-- return errors, but this also makes the contract safe for platform adapters
|
|
-- that surface I/O failures as Lua errors.
|
|
local function withVersionPrefix(version, fs, action)
|
|
local saved = fs.prefix
|
|
fs.prefix = GameVersion.cachePrefix(version)
|
|
local ok, first, second = pcall(action)
|
|
fs.prefix = saved
|
|
if not ok then return false, first end
|
|
return true, first, second
|
|
end
|
|
|
|
local function nativeRs(version)
|
|
return version == "ruby" or version == "sapphire"
|
|
end
|
|
|
|
local function nativeMeta(fs)
|
|
local raw = fs.read and fs.read("data/generated/gba/meta.json")
|
|
if type(raw) ~= "string" then return nil end
|
|
local ok, meta = pcall(require("src.link.Json").decode, raw)
|
|
if ok and type(meta) == "table" then return meta end
|
|
end
|
|
|
|
local function nativeDataReady(version, fs, sha1)
|
|
if not nativeRs(version) then return true end
|
|
local path = "data/generated/gba/meta.json"
|
|
if sha1 ~= nil and type(sha1) ~= "string" then return false, path end
|
|
local meta = nativeMeta(fs)
|
|
local V = require("src.import.gba.versions").forGame(version)
|
|
if not meta or meta.version ~= version or meta.cache_version ~= V.CACHE_VERSION
|
|
or meta.native_version ~= V.NATIVE_VERSION
|
|
or type(meta.romSha1) ~= "string" then return false, path end
|
|
if meta.md5 ~= nil and (type(meta.md5) ~= "string"
|
|
or meta.md5:lower() ~= meta.romSha1:lower()) then return false, path end
|
|
local expected = type(sha1) == "string" and sha1:lower() or meta.romSha1:lower()
|
|
if meta.romSha1:lower() ~= expected then return false, path end
|
|
return require("src.import.gba.rs.cache_readiness").check(version, fs, expected)
|
|
end
|
|
|
|
function CacheContract.allRequiredFilesExist(version, fs, sha1)
|
|
fs = fs or require("src.import.CacheFs")
|
|
local ok, complete, missing = withVersionPrefix(version, fs, function()
|
|
local required, isOverride = CacheContract.requiredFilesFor(version)
|
|
local missingPath
|
|
for _, path in ipairs(required) do
|
|
if not fs.exists(path) then missingPath = path; break end
|
|
end
|
|
if not missingPath and not isOverride then
|
|
for _, path in ipairs(CacheContract.VERSION_REQUIRED_FILES[version] or {}) do
|
|
if not fs.exists(path) then missingPath = path; break end
|
|
end
|
|
end
|
|
if missingPath then return false, missingPath end
|
|
return nativeDataReady(version, fs, sha1)
|
|
end)
|
|
if not ok then return false, complete end
|
|
return complete, missing
|
|
end
|
|
|
|
function CacheContract.readMarker(version, fs)
|
|
fs = fs or require("src.import.CacheFs")
|
|
local ok, marker, readError = withVersionPrefix(version, fs, function()
|
|
return fs.read(CacheContract.MARKER_PATH)
|
|
end)
|
|
if not ok then return nil, marker end
|
|
-- LÖVE may return contents plus a byte count; only a nil contents result
|
|
-- makes the auxiliary value an error. CacheFs' portable reader returns
|
|
-- just the contents, so this remains adapter-neutral.
|
|
if marker == nil then return nil, readError end
|
|
return marker
|
|
end
|
|
|
|
function CacheContract.cacheVersionCurrent(version, fs)
|
|
if GameVersion.generation(version) ~= 3 then return true end
|
|
fs = fs or require("src.import.CacheFs")
|
|
local okV, Versions = pcall(function()
|
|
return require("src.import.gba.versions").forGame(version)
|
|
end)
|
|
if not okV or not Versions or not Versions.CACHE_VERSION then return true end
|
|
local ok, raw = withVersionPrefix(version, fs, function()
|
|
return fs.read("data/generated/gba/meta.json")
|
|
end)
|
|
if not ok or type(raw) ~= "string" then return false end
|
|
local stamped = tonumber(raw:match('"cache_version"%s*:%s*(%d+)'))
|
|
return stamped == Versions.CACHE_VERSION
|
|
end
|
|
|
|
function CacheContract.isReady(version, fs)
|
|
fs = fs or require("src.import.CacheFs")
|
|
if CacheContract.sourceTreeHasData(version) then return true end
|
|
local marker, readError = CacheContract.readMarker(version, fs)
|
|
if readError or not CacheContract.markerMatches(version, marker) then return false end
|
|
if not CacheContract.cacheVersionCurrent(version, fs) then return false end
|
|
local sha1 = nativeRs(version) and marker:match(":([%x]+)$") or nil
|
|
return CacheContract.allRequiredFilesExist(version, fs, sha1)
|
|
end
|
|
|
|
function CacheContract.publish(version, fs, sha1)
|
|
fs = fs or require("src.import.CacheFs")
|
|
if nativeRs(version) then
|
|
if sha1 == nil then
|
|
local ok, meta = withVersionPrefix(version, fs, function() return nativeMeta(fs) end)
|
|
sha1 = ok and meta and meta.romSha1 or nil
|
|
end
|
|
if type(sha1) == "string" then sha1 = sha1:lower() end
|
|
end
|
|
local complete, missing = CacheContract.allRequiredFilesExist(version, fs, sha1)
|
|
if not complete then
|
|
-- A caller may be retrying over a partially replaced cache. Do not
|
|
-- leave its old marker advertising readiness after this failed check.
|
|
local removed, removeError = withVersionPrefix(version, fs, function()
|
|
if not fs.remove then
|
|
error("cache filesystem cannot remove the completion marker")
|
|
end
|
|
return fs.remove(CacheContract.MARKER_PATH)
|
|
end)
|
|
if not removed then
|
|
return false, "cache is incomplete; missing " .. tostring(missing)
|
|
.. "; could not remove completion marker: " .. tostring(removeError)
|
|
end
|
|
return false, "cache is incomplete; missing " .. tostring(missing)
|
|
end
|
|
local changed, ok, err = withVersionPrefix(version, fs, function()
|
|
return fs.write(CacheContract.MARKER_PATH, CacheContract.markerFor(version, sha1))
|
|
end)
|
|
if not changed then return false, tostring(ok) end
|
|
return ok, err
|
|
end
|
|
|
|
function CacheContract.sourceTreeHasData(version)
|
|
if not (love and love.filesystem and love.filesystem.getInfo
|
|
and love.filesystem.getRealDirectory and love.filesystem.getSource) then
|
|
return false
|
|
end
|
|
local prefix = version == "red" and "" or GameVersion.cachePrefix(version)
|
|
local required, isOverride = CacheContract.requiredFilesFor(version)
|
|
local source = love.filesystem.getSource()
|
|
for _, path in ipairs(required) do
|
|
local fullPath = prefix .. path
|
|
if love.filesystem.getInfo(fullPath, "file") == nil
|
|
or love.filesystem.getRealDirectory(fullPath) ~= source then
|
|
return false
|
|
end
|
|
end
|
|
if not isOverride then
|
|
for _, path in ipairs(CacheContract.VERSION_REQUIRED_FILES[version] or {}) do
|
|
local fullPath = prefix .. path
|
|
if love.filesystem.getInfo(fullPath, "file") == nil
|
|
or love.filesystem.getRealDirectory(fullPath) ~= source then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
if nativeRs(version) then
|
|
local exact = {
|
|
read = function(path)
|
|
local full = prefix .. path
|
|
if love.filesystem.getRealDirectory(full) == source then return love.filesystem.read(full) end
|
|
end,
|
|
exists = function(path)
|
|
local full = prefix .. path
|
|
return love.filesystem.getInfo(full, "file") ~= nil
|
|
and love.filesystem.getRealDirectory(full) == source
|
|
end,
|
|
}
|
|
return nativeDataReady(version, exact)
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- Inspect another version without mutating CacheFs.prefix, GameVersion, mounts,
|
|
-- or the active Data table. The injected filesystem addresses exact paths.
|
|
local function readAt(fs, path)
|
|
if fs.readAt then return fs.readAt(path) end
|
|
if fs.read then return fs.read(path) end
|
|
return nil
|
|
end
|
|
|
|
local function isFileAt(fs, path)
|
|
if fs.getInfo then
|
|
local info = fs.getInfo(path, "file")
|
|
return info ~= nil and (info.type == nil or info.type == "file")
|
|
end
|
|
if fs.existsAt then return fs.existsAt(path) == true end
|
|
return false
|
|
end
|
|
|
|
local function hasExactFiles(version, fs, prefix, semantic)
|
|
for _, path in ipairs(CacheContract.requiredFiles(version, semantic)) do
|
|
if not isFileAt(fs, prefix .. path) then return false, path end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function sourceReady(version, fs, semantic)
|
|
if not (fs.getInfo and fs.getRealDirectory and fs.getSource) then return nil end
|
|
local prefix = version == "red" and "" or GameVersion.cachePrefix(version)
|
|
local complete = hasExactFiles(version, fs, prefix, semantic)
|
|
if not complete then return nil end
|
|
local first = prefix .. CacheContract.requiredFiles(version, semantic)[1]
|
|
if fs.getRealDirectory(first) ~= fs.getSource() then return nil end
|
|
if nativeRs(version) then
|
|
local source = fs.getSource()
|
|
for _, path in ipairs(CacheContract.requiredFiles(version, semantic)) do
|
|
if fs.getRealDirectory(prefix .. path) ~= source then return nil end
|
|
end
|
|
local relative = {
|
|
read = function(path)
|
|
local full = prefix .. path
|
|
if fs.getRealDirectory(full) == source then return readAt(fs, full) end
|
|
end,
|
|
exists = function(path)
|
|
local full = prefix .. path
|
|
return isFileAt(fs, full) and fs.getRealDirectory(full) == source
|
|
end,
|
|
}
|
|
if not nativeDataReady(version, relative) then return nil end
|
|
end
|
|
return { kind = "source", prefix = prefix }
|
|
end
|
|
|
|
function CacheContract.inspect(version, fs, opts)
|
|
opts = opts or {}
|
|
if not (GameVersion.VERSIONS[version] and fs) then
|
|
return nil, "not_imported", "unsupported cache inspection"
|
|
end
|
|
if opts.allowSource then
|
|
local source = sourceReady(version, fs, opts.semantic)
|
|
if source then return source end
|
|
end
|
|
local prefix = GameVersion.cachePrefix(version)
|
|
local marker = readAt(fs, prefix .. CacheContract.MARKER_PATH)
|
|
if not CacheContract.markerMatches(version, marker) then
|
|
return nil, "not_imported", "completion marker is missing or stale"
|
|
end
|
|
local complete, missing = hasExactFiles(version, fs, prefix, opts.semantic)
|
|
if not complete then
|
|
return nil, "not_imported", "required cache file is missing: " .. tostring(missing)
|
|
end
|
|
if nativeRs(version) then
|
|
local relative = {
|
|
read = function(path) return readAt(fs, prefix .. path) end,
|
|
exists = function(path) return isFileAt(fs, prefix .. path) end,
|
|
}
|
|
local ready, invalid = nativeDataReady(version, relative, marker:match(":([%x]+)$"))
|
|
if not ready then return nil, "not_imported", "native cache data is missing or stale: " .. tostring(invalid) end
|
|
end
|
|
return { kind = "cache", prefix = prefix, marker = marker }
|
|
end
|
|
|
|
return CacheContract
|