mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-26 07:21:22 +02:00
Merge branch 'dev' of https://github.com/bryanthaboi/gen1recomp into dev
This commit is contained in:
+25
-3
@@ -34,6 +34,28 @@ DATASETS = (
|
||||
"text", "field", "battle_anims",
|
||||
)
|
||||
|
||||
# Sound is decoded by the Lua importer (src/import/RomExtractor.lua), not here,
|
||||
# so --clean must step around it rather than delete what it cannot rebuild.
|
||||
UNOWNED_DATA = ("audio.lua",)
|
||||
UNOWNED_ASSETS = ("audio",)
|
||||
|
||||
|
||||
def clean_generated(out_dir, assets_dir):
|
||||
"""Empty the generated dirs, keeping artifacts this tool never writes."""
|
||||
kept = []
|
||||
for path, spared in ((out_dir, UNOWNED_DATA), (assets_dir, UNOWNED_ASSETS)):
|
||||
if not os.path.isdir(path):
|
||||
continue
|
||||
for name in os.listdir(path):
|
||||
target = os.path.join(path, name)
|
||||
if name in spared:
|
||||
kept.append(target)
|
||||
elif os.path.isdir(target):
|
||||
shutil.rmtree(target)
|
||||
else:
|
||||
os.remove(target)
|
||||
return kept
|
||||
|
||||
_TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
VERSION_MANIFESTS = {
|
||||
"red": os.path.join(_TOOLS_DIR, "rom_manifest.json"),
|
||||
@@ -2207,9 +2229,9 @@ def main(argv=None):
|
||||
out_dir = args.out or prefix + os.path.join("data", "generated")
|
||||
assets_dir = args.assets or prefix + os.path.join("assets", "generated")
|
||||
if args.clean:
|
||||
for path in (out_dir, assets_dir):
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
kept = clean_generated(out_dir, assets_dir)
|
||||
for path in kept:
|
||||
print(f"kept {path} (not produced by this tool)")
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
os.makedirs(assets_dir, exist_ok=True)
|
||||
datasets = tuple(args.only) if args.only else DATASETS
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Existing v<version> tag to build and publish."
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
CART_ID: "{{CART_ID}}"
|
||||
CARTKIT_REPO: bryanthaboi/gen1recomp
|
||||
CARTKIT_REF: dev
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch cartkit
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl -fsSL --retry 3 -o "$RUNNER_TEMP/cartkit.py" \
|
||||
"https://raw.githubusercontent.com/${CARTKIT_REPO}/${CARTKIT_REF}/tools/cartkit.py"
|
||||
python3 "$RUNNER_TEMP/cartkit.py" selftest --quiet
|
||||
|
||||
- name: Check the tag against cart.json
|
||||
id: cart
|
||||
env:
|
||||
TAG: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY' >> "$GITHUB_OUTPUT"
|
||||
import json, os, sys
|
||||
|
||||
with open("cart.json", encoding="utf-8") as fh:
|
||||
cart = json.load(fh)
|
||||
version = str(cart.get("version", ""))
|
||||
cart_id = str(cart.get("id", ""))
|
||||
tag = os.environ["TAG"]
|
||||
if tag != f"v{version}":
|
||||
print(f"::error::tag {tag} does not match cart.json version "
|
||||
f"{version} (expected v{version})", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
stamped = os.environ["CART_ID"]
|
||||
if cart_id != stamped:
|
||||
print(f"::error::cart.json id is {cart_id}, but this workflow "
|
||||
f"was stamped for {stamped}; rerun cartkit "
|
||||
"add-release-workflow", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
print(f"version={version}")
|
||||
print(f"id={cart_id}")
|
||||
print(f"tag={tag}")
|
||||
PY
|
||||
|
||||
- name: Validate every pin
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: python3 "$RUNNER_TEMP/cartkit.py" validate . --online --strict
|
||||
|
||||
- name: Pack the cart
|
||||
env:
|
||||
CART_VERSION: ${{ steps.cart.outputs.version }}
|
||||
CART_NAME: ${{ steps.cart.outputs.id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
out="$GITHUB_WORKSPACE/dist"
|
||||
rm -rf "$out"
|
||||
mkdir -p "$out"
|
||||
python3 "$RUNNER_TEMP/cartkit.py" pack . \
|
||||
-o "$out/${CART_NAME}-${CART_VERSION}.g1rcart"
|
||||
(cd "$out" && sha256sum ./*.g1rcart > sha256sums.txt)
|
||||
cat "$out/sha256sums.txt"
|
||||
|
||||
- name: Publish GitHub Release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
CART_VERSION: ${{ steps.cart.outputs.version }}
|
||||
CART_NAME: ${{ steps.cart.outputs.id }}
|
||||
TAG: ${{ steps.cart.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
prev="$(git tag -l 'v*' --sort=-v:refname | grep -v "^${TAG}$" | head -1 || true)"
|
||||
range="${prev:+${prev}..}${TAG}"
|
||||
changes="$(git log --no-merges --pretty='- %s' "$range" | head -50 || true)"
|
||||
|
||||
notes=$'Download the .g1rcart and open it from the game to install this cart.'
|
||||
notes+=$'\n\nThe cart is a manifest: it pins each mod to the exact build listed in cart.json and ships no code of its own.'
|
||||
if [ -n "$changes" ]; then
|
||||
notes+=$'\n\n## Changes\n\n'"$changes"
|
||||
fi
|
||||
|
||||
asset="dist/${CART_NAME}-${CART_VERSION}.g1rcart"
|
||||
if gh release view "$TAG" >/dev/null 2>&1; then
|
||||
gh release upload "$TAG" "$asset" "dist/sha256sums.txt" --clobber
|
||||
else
|
||||
gh release create "$TAG" \
|
||||
--target "$GITHUB_SHA" \
|
||||
--title "$CART_VERSION" \
|
||||
--notes "$notes" \
|
||||
"$asset" \
|
||||
"dist/sha256sums.txt"
|
||||
fi
|
||||
|
||||
echo "Published $TAG"
|
||||
+1770
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
"""Crystal intro-movie and title-screen symbols for the Crystal manifest.
|
||||
|
||||
Gold's title/intro symbols do not exist in Crystal: CrystalIntro is a different
|
||||
program with its own asset set (engine/movie/intro.asm:1678-1777) and the title
|
||||
composes on the fly with no tilemap (engine/movie/title.asm:364-374).
|
||||
"""
|
||||
|
||||
# engine/movie/intro.asm:1678-1777
|
||||
INTRO_SYMBOLS = [
|
||||
"IntroSuicuneRunGFX",
|
||||
"IntroPichuWooperGFX",
|
||||
"IntroBackgroundGFX",
|
||||
"IntroBackgroundTilemap",
|
||||
"IntroBackgroundAttrmap",
|
||||
"IntroBackgroundPalette",
|
||||
"IntroUnownsGFX",
|
||||
"IntroPulseGFX",
|
||||
"IntroUnownATilemap",
|
||||
"IntroUnownAAttrmap",
|
||||
"IntroUnownHITilemap",
|
||||
"IntroUnownHIAttrmap",
|
||||
"IntroUnownsTilemap",
|
||||
"IntroUnownsAttrmap",
|
||||
"IntroUnownsPalette",
|
||||
"IntroCrystalUnownsGFX",
|
||||
"IntroCrystalUnownsTilemap",
|
||||
"IntroCrystalUnownsAttrmap",
|
||||
"IntroCrystalUnownsPalette",
|
||||
"IntroSuicuneCloseGFX",
|
||||
"IntroSuicuneCloseTilemap",
|
||||
"IntroSuicuneCloseAttrmap",
|
||||
"IntroSuicuneClosePalette",
|
||||
"IntroSuicuneJumpGFX",
|
||||
"IntroSuicuneBackGFX",
|
||||
"IntroSuicuneJumpTilemap",
|
||||
"IntroSuicuneJumpAttrmap",
|
||||
"IntroSuicuneBackTilemap",
|
||||
"IntroSuicuneBackAttrmap",
|
||||
"IntroSuicunePalette",
|
||||
"IntroUnownBackGFX",
|
||||
"IntroGrass1GFX",
|
||||
"IntroGrass2GFX",
|
||||
"IntroGrass3GFX",
|
||||
"IntroGrass4GFX",
|
||||
]
|
||||
|
||||
# Palette fades that live as local labels inside their scene routines.
|
||||
# engine/movie/intro.asm:1189 (fade.pal), :1385-1388 (unown_1.pal / unown_2.pal)
|
||||
INTRO_FADE_SYMBOLS = [
|
||||
"Intro_Scene24_ApplyPaletteFade.FadePals",
|
||||
"Intro_Scene20_AppearUnown.pal1",
|
||||
"Intro_Scene20_AppearUnown.pal2",
|
||||
"Intro_FadeUnownWordPals.FastFadePalettes",
|
||||
"Intro_FadeUnownWordPals.SlowFadePalettes",
|
||||
]
|
||||
|
||||
# engine/movie/title.asm:364-374
|
||||
TITLE_SYMBOLS = [
|
||||
"TitleSuicuneGFX",
|
||||
"TitleLogoGFX",
|
||||
"TitleCrystalGFX",
|
||||
"TitleScreenPalettes",
|
||||
]
|
||||
|
||||
# engine/movie/splash.asm:344 -- replaces Gold's GameFreakLogoStarsGFX.
|
||||
# The Ditto OBJ palette is a local label (engine/gfx/cgb_layouts.asm:876-893).
|
||||
SPLASH_SYMBOLS = [
|
||||
"GameFreakDittoGFX",
|
||||
"GameFreakDittoPaletteFade",
|
||||
"_CGB_GamefreakLogo.GamefreakDittoPalette",
|
||||
]
|
||||
|
||||
MOVIE_SYMBOLS = (
|
||||
INTRO_SYMBOLS + INTRO_FADE_SYMBOLS + TITLE_SYMBOLS + SPLASH_SYMBOLS)
|
||||
@@ -0,0 +1,149 @@
|
||||
"""REQUIRED_SYMBOLS delta between pokegold.sym and pokecrystal.sym.
|
||||
|
||||
make_gold_manifest.REQUIRED_SYMBOLS names 341 symbols by hand. 25 of them do
|
||||
not exist in pokecrystal.sym: Crystal renamed the credits mons, split the
|
||||
trainer-card / Pokegear / pack-pals blocks by player gender, split the two
|
||||
FontsExtra tiles apart, and replaced the whole Gold/Silver intro movie and
|
||||
title screen. DROP_SYMBOLS lists those 25 and ADD_SYMBOLS the verified
|
||||
replacements; the intro, title and splash names come from
|
||||
crystal_movie_symbols.MOVIE_SYMBOLS (that file is the pinned CT-9 list and is
|
||||
not edited here). MOBILE_SYMBOLS is the Mobile System GB art, which no Gold
|
||||
or Silver ROM carries at all.
|
||||
|
||||
Every name below was checked against ../pokecrystal-symbols/pokecrystal.sym.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from crystal_movie_symbols import MOVIE_SYMBOLS
|
||||
|
||||
# Gold-only names, grouped by what replaces them.
|
||||
DROP_SYMBOLS = frozenset({
|
||||
# engine/movie/title.asm:364-373 -- no tilemap; DrawTitleGraphic composes.
|
||||
"TitleScreenGFX1", "TitleScreenGFX2", "TitleScreenGFX3",
|
||||
"TitleScreenGFX4", "TitleScreenTilemap",
|
||||
# engine/movie/credits.asm:610-613
|
||||
"CreditsBellossomGFX", "CreditsTogepiGFX",
|
||||
"CreditsElekidGFX", "CreditsSentretGFX",
|
||||
# gfx/misc.asm:44
|
||||
"GameFreakLogoStarsGFX",
|
||||
# engine/gfx/player_gfx.asm:114,117,120,203,206
|
||||
"ChrisPicAndTrainerCardGFX",
|
||||
# engine/gfx/color.asm:1330,1333
|
||||
"PokegearPals",
|
||||
# engine/gfx/cgb_layouts.asm:818,821
|
||||
"_CGB_PackPals.PackPals",
|
||||
# gfx/font.asm:51,63
|
||||
"FontsExtra_SolidBlackAndUpArrowGFX",
|
||||
# engine/movie/intro.asm:1 -- CrystalIntro is a different program.
|
||||
"Intro_WaterGFX1", "Intro_WaterTilemap", "Intro_WaterMeta",
|
||||
"Intro_WaterGFX2",
|
||||
"Intro_GrassGFX1", "Intro_GrassTilemap", "Intro_GrassMeta",
|
||||
"Intro_GrassGFX2",
|
||||
"Intro_FireGFX1", "Intro_FireGFX2", "Intro_FireGFX3",
|
||||
})
|
||||
|
||||
# Crystal replacements, minus the intro/title/splash ones MOVIE_SYMBOLS owns.
|
||||
ADD_SYMBOLS = frozenset({
|
||||
# engine/movie/credits.asm:610-613
|
||||
"CreditsPichuGFX", "CreditsSmoochumGFX",
|
||||
"CreditsDittoGFX", "CreditsIgglybuffGFX",
|
||||
# engine/gfx/player_gfx.asm:114,117,120,203,206
|
||||
"ChrisCardPic", "KrisCardPic", "TrainerCardGFX", "ChrisPic", "KrisPic",
|
||||
# engine/gfx/color.asm:1330,1333
|
||||
"MalePokegearPals", "FemalePokegearPals",
|
||||
# engine/gfx/cgb_layouts.asm:818,821
|
||||
"_CGB_PackPals.ChrisPackPals", "_CGB_PackPals.KrisPackPals",
|
||||
# engine/events/fishing_gfx.asm:41 -- Kris' half of the fishing pose.
|
||||
"KrisFishingGFX",
|
||||
# gfx/font.asm:51,63 -- black is 1bpp at tile $60, up_arrow 2bpp at $61.
|
||||
"FontsExtra_SolidBlackGFX", "FontsExtra2_UpArrowGFX",
|
||||
# main.asm:425-448 -- the pic-animation pointer tables, absent from Gold.
|
||||
"AnimationPointers", "AnimationIdlePointers",
|
||||
"BitmasksPointers", "FramesPointers",
|
||||
"UnownAnimationPointers", "UnownAnimationIdlePointers",
|
||||
"UnownBitmasksPointers", "UnownFramesPointers",
|
||||
# engine/tilesets/tileset_anims.asm -- the five Crystal-only anim steps.
|
||||
"AnimateFountainTile",
|
||||
"ForestTreeLeftAnimation", "ForestTreeRightAnimation",
|
||||
"ForestTreeLeftAnimation2", "ForestTreeRightAnimation2",
|
||||
# engine/overworld/wildmons.asm:493-524 -- Raikou and Entei only.
|
||||
"InitRoamMons",
|
||||
# data/events/unown_walls.asm:7,15 -- the four Ruins of Alph wall words
|
||||
# and the menu box each one is drawn in.
|
||||
"UnownWalls", "MenuHeaders_UnownWalls",
|
||||
# data/battle_tower/classes.asm:6 and data/battle_tower/parties.asm:1 --
|
||||
# the Battle Tower roster, which no Gold or Silver ROM carries; the
|
||||
# sprites table is engine/events/battle_tower/battle_tower.asm:1578
|
||||
# INCLUDE "data/trainers/sprites.asm".
|
||||
"BattleTowerTrainers", "BattleTowerMons", "BTTrainerClassSprites",
|
||||
# data/battle_tower/unknown.asm:1, copied into wBT_OTTrainerData whole.
|
||||
"BattleTowerTrainerData",
|
||||
# engine/events/battle_tower/load_trainer.asm:24,112 -- the two rejection
|
||||
# loops whose `maskbits` / `cp` pair carries the sample ceiling. Crystal
|
||||
# 1.0 and 1.1 differ only in the trainer one (:29-37), so it is read out
|
||||
# of the cart rather than written down.
|
||||
"LoadOpponentTrainerAndPokemon.resample",
|
||||
"LoadRandomBattleTowerMon.resample",
|
||||
})
|
||||
|
||||
# Mobile System GB art, all of it present in the international v1.0 object:
|
||||
# mobile/*.asm assembles there, only the menus that reach it are Japan-only.
|
||||
MOBILE_SYMBOLS = frozenset({
|
||||
# mobile/mobile_5c.asm:290,293,296,753,756-771,866,874,878
|
||||
"AsciiFontGFX", "PichuAnimatedMobileGFX", "ElectroBallMobileGFX",
|
||||
"PichuBorderMobileGFX", "Stadium2N64GFX", "Stadium2N64Tilemap",
|
||||
"Stadium2N64Attrmap", "PasswordTopTilemap", "PasswordBottomTilemap",
|
||||
"PasswordShiftTilemap", "ChooseMobileCenterTilemap",
|
||||
"MobilePasswordAttrmap", "ChooseMobileCenterAttrmap",
|
||||
"MobilePasswordPalettes",
|
||||
# mobile/mobile_5e.asm:2,5,8,11,14,18,925,928,931,934,941
|
||||
"MobileCardGFX", "ChrisSilhouetteGFX", "KrisSilhouetteGFX",
|
||||
"MobileCard2GFX", "CardLargeSpriteAndFolderGFX", "CardSpriteGFX",
|
||||
"DialpadTilemap", "DialpadAttrmap", "DialpadGFX", "DialpadCursorGFX",
|
||||
"MobileCardListGFX",
|
||||
# mobile/mobile_5f.asm:84,87,91,3528,3534,3537
|
||||
"HaveWantGFX", "MobileSelectGFX", "HaveWantMap", "PokemonNewsGFX",
|
||||
"PokemonNewsTileAttrmap", "PokemonNewsPalettes",
|
||||
# mobile/mobile_5b.asm:206,209,212,215,758
|
||||
"MobileSystemSplashScreen_InitGFX.Tiles",
|
||||
"MobileSystemSplashScreen_InitGFX.Tilemap",
|
||||
"MobileSystemSplashScreen_InitGFX.Attrmap",
|
||||
"MobileSplashScreenPalettes", "MobileAdapterCheckGFX",
|
||||
# mobile/mobile_42.asm:1733-1763 and mobile/mobile_40.asm:6921,6924
|
||||
"MobileTradeSpritesGFX", "MobileTradeGFX", "MobileTradeTilemapLZ",
|
||||
"MobileTradeAttrmapLZ", "MobileCable1GFX", "MobileCable2GFX",
|
||||
"UnusedMobilePulsePalettes", "MobileTradeBGPalettes",
|
||||
"MobileTradeOB1Palettes", "MobileTradeOB2Palettes",
|
||||
"MobileAdapterPalettes", "MobileTradeLightsGFX",
|
||||
"MobileTradeLightsPalettes",
|
||||
# mobile/mobile_45_2.asm:1362,1365,1368 and
|
||||
# mobile/mobile_45_sprite_engine.asm:311
|
||||
"PichuBorderMobileOBPalettes", "PichuBorderMobileBGPalettes",
|
||||
"PichuBorderMobileTilemapAttrmap", "MobileDialingGFX",
|
||||
# mobile/mobile_12.asm:1007,1010, mobile/mobile_22.asm:518,
|
||||
# mobile/mobile_41.asm:1115, mobile/fixed_words.asm:3231
|
||||
"MobileUpArrowGFX", "MobileDownArrowGFX", "EZChatCursorGFX",
|
||||
"MobileDialingFrameGFX", "SelectStartGFX",
|
||||
# engine/menus/main_menu.asm:24 and gfx/font.asm:58
|
||||
"MobileMenuGFX", "MobilePhoneTilesGFX",
|
||||
# engine/link/mystery_gift.asm:1606,1920,1923
|
||||
"MysteryGiftGFX", "CardTradeGFX", "CardTradeSpriteGFX",
|
||||
})
|
||||
|
||||
|
||||
def crystal_required(gold_required):
|
||||
"""Apply the delta to make_gold_manifest.REQUIRED_SYMBOLS."""
|
||||
gold = frozenset(gold_required)
|
||||
stale = sorted(DROP_SYMBOLS - gold)
|
||||
if stale:
|
||||
raise SystemExit(
|
||||
"crystal_symbol_deltas: DROP_SYMBOLS names that Gold no longer "
|
||||
"requires: " + ", ".join(stale))
|
||||
extra = ADD_SYMBOLS | MOBILE_SYMBOLS | set(MOVIE_SYMBOLS)
|
||||
collisions = sorted(extra & gold)
|
||||
if collisions:
|
||||
raise SystemExit(
|
||||
"crystal_symbol_deltas: ADD names already in the Gold set: "
|
||||
+ ", ".join(collisions))
|
||||
return frozenset((gold - DROP_SYMBOLS) | extra)
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate tools/rom_manifest_crystal.json from pret/pokecrystal.
|
||||
|
||||
Crystal is its own pret tree, but the manifest SHAPE is Gold's: same constant
|
||||
blocks, same charmap parser, same symbol resolution. So this drives
|
||||
make_gold_manifest.generate() over ../pokecrystal instead of forking it, the
|
||||
way make_yellow_manifest.py drives make_rom_manifest's helpers over
|
||||
pokeyellow. Three things are passed in:
|
||||
|
||||
* defines -- the EMPTY set. ../pokecrystal/Makefile:129 builds the retail
|
||||
international v1.0 object with no -D at all (:130-134 add
|
||||
_CRYSTAL11 / _CRYSTAL_AU / _DEBUG / _CRYSTAL11_VC for the four
|
||||
other targets). Leaving Gold's {"_GOLD"} in place would take
|
||||
an `IF DEF(_GOLD)` arm that Crystal never assembles.
|
||||
* required -- crystal_symbol_deltas.crystal_required(), the Gold symbol set
|
||||
with the 25 Gold-only names swapped for their Crystal
|
||||
replacements plus crystal_movie_symbols.MOVIE_SYMBOLS.
|
||||
* sha1 -- the Crystal cart hash.
|
||||
|
||||
`anim_labels` is on: Crystal has per-species pic-animation tables
|
||||
(../pokecrystal/main.asm:425-448) and Gold has none.
|
||||
|
||||
Crystal also declares two extra RGBDS charmaps that Gold has not got at all
|
||||
(../pokecrystal/constants/charmap.asm:422-442): `unown`, used by the Ruins of
|
||||
Alph wall words, and `ascii`, used only by the Mobile System GB code. Neither
|
||||
is more rows of the main charmap -- the same byte means a different thing in
|
||||
each -- so make_gold_manifest.charmap deliberately STOPS at the first
|
||||
`newcharmap`, and that guard has to keep protecting the main table. The Unown
|
||||
one is carried here instead, as its own top-level `unownCharmap` key: that is
|
||||
where `charmap` and `fontCharmap` already live, so a consumer picks the map it
|
||||
wants by name and no reader of the main table can see these bytes. The `ascii`
|
||||
map is not emitted, because nothing outside the Mobile System reads it.
|
||||
|
||||
Usage: python3 tools/make_crystal_manifest.py
|
||||
Default paths: pokecrystal at ../pokecrystal (relative to the repo) or
|
||||
/Users/bryanbassett/Documents/development/pokecrystal; symbols at
|
||||
/Users/bryanbassett/Documents/development/pokecrystal-symbols/pokecrystal.sym.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
import make_gold_manifest as gold # noqa: E402
|
||||
from crystal_symbol_deltas import crystal_required # noqa: E402
|
||||
from rom_data import CANONICAL_CRYSTAL_SHA1 # noqa: E402
|
||||
|
||||
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DEV = "/Users/bryanbassett/Documents/development"
|
||||
DEFAULT_POKECRYSTAL_CANDIDATES = [
|
||||
os.path.join(os.path.dirname(REPO_ROOT), "pokecrystal"),
|
||||
os.path.join(DEV, "pokecrystal"),
|
||||
]
|
||||
DEFAULT_SYMBOLS = os.path.join(DEV, "pokecrystal-symbols/pokecrystal.sym")
|
||||
DEFAULT_OUT = os.path.join(
|
||||
os.path.dirname(__file__), "rom_manifest_crystal.json")
|
||||
|
||||
# ../pokecrystal/Makefile:129
|
||||
CRYSTAL_ASM_DEFINES = frozenset()
|
||||
|
||||
CRYSTAL_REQUIRED_SYMBOLS = crystal_required(gold.REQUIRED_SYMBOLS)
|
||||
|
||||
|
||||
def _rgbds_int(expr, i):
|
||||
"""Evaluate one `charmap` value expression for loop counter `i`.
|
||||
|
||||
RGBDS `$xx` is hex and `/` is integer division; nothing else in the block
|
||||
needs modelling. The whitelist is what keeps this from being eval() on
|
||||
arbitrary asm.
|
||||
"""
|
||||
text = re.sub(r"\$([0-9a-fA-F]+)", lambda m: str(int(m.group(1), 16)), expr)
|
||||
text = text.replace("/", "//")
|
||||
if not re.fullmatch(r"[0-9i()+\-*/ ]+", text):
|
||||
raise ValueError(f"unsupported charmap expression: {expr}")
|
||||
return int(eval(text, {"__builtins__": {}}, {"i": i})) # noqa: S307
|
||||
|
||||
|
||||
def unown_charmap(pokecrystal, defines=None):
|
||||
"""The `unown` charmap: byte -> letter, same shape as gold.charmap.
|
||||
|
||||
../pokecrystal/constants/charmap.asm:422-431 is a `pushc` block that names
|
||||
the map, DEFs a string of every printable character, and emits one
|
||||
`charmap STRSLICE(...)` per character inside a `for`. So the parse has to
|
||||
follow the loop rather than read literal rows -- but it still reads the
|
||||
letter set and the tile arithmetic out of the asm, not out of a copy here.
|
||||
"""
|
||||
path = os.path.join(pokecrystal, "constants", "charmap.asm")
|
||||
out, strings, inside, loop = {}, {}, False, None
|
||||
for _, line in gold.read_asm(path, defines):
|
||||
s = line.strip()
|
||||
if re.match(r"newcharmap\s+unown\b", s):
|
||||
inside = True
|
||||
continue
|
||||
if not inside:
|
||||
continue
|
||||
if re.match(r"(popc|newcharmap)\b", s):
|
||||
break
|
||||
m = re.match(r'DEF\s+(\w+)\s+EQUS\s+"(.*)"$', s)
|
||||
if m:
|
||||
strings[m.group(1)] = m.group(2)
|
||||
continue
|
||||
m = re.match(r"for\s+(\w+),\s*STRLEN\(#(\w+)\)\s*$", s)
|
||||
if m:
|
||||
loop = (m.group(1), strings[m.group(2)])
|
||||
continue
|
||||
if re.match(r"endr\s*$", s):
|
||||
loop = None
|
||||
continue
|
||||
m = re.match(
|
||||
r"charmap\s+STRSLICE\(#(\w+),\s*\w+,\s*\w+\s*\+\s*1\),\s*(.+)$", s)
|
||||
if m and loop:
|
||||
for index, char in enumerate(strings[m.group(1)]):
|
||||
out[str(_rgbds_int(m.group(2), index))] = char
|
||||
continue
|
||||
m = re.match(r'charmap\s+"(.*)",\s*(\$[0-9a-fA-F]+)$', s)
|
||||
if m:
|
||||
out[str(int(m.group(2)[1:], 16))] = m.group(1)
|
||||
if not out:
|
||||
raise SystemExit("charmap.asm has no `unown` charmap")
|
||||
return out
|
||||
|
||||
|
||||
def generate(pokecrystal, symbols_path):
|
||||
data = gold.generate(
|
||||
pokecrystal, symbols_path,
|
||||
defines=CRYSTAL_ASM_DEFINES,
|
||||
required=CRYSTAL_REQUIRED_SYMBOLS,
|
||||
sha1=CANONICAL_CRYSTAL_SHA1,
|
||||
anim_labels=True)
|
||||
# Crystal renumbers the block: 162 flags to Gold's 93, so a consumer that
|
||||
# hardcodes Gold's indices reads the wrong flag.
|
||||
data["constants"]["engineFlagOrder"] = gold.parse_const_block(
|
||||
os.path.join(pokecrystal, "constants", "engine_flags.asm"),
|
||||
defines=CRYSTAL_ASM_DEFINES)
|
||||
data["unownCharmap"] = unown_charmap(pokecrystal, CRYSTAL_ASM_DEFINES)
|
||||
return data
|
||||
|
||||
|
||||
def find_pokecrystal():
|
||||
for candidate in DEFAULT_POKECRYSTAL_CANDIDATES:
|
||||
if os.path.isfile(os.path.join(candidate, "main.asm")):
|
||||
return candidate
|
||||
return DEFAULT_POKECRYSTAL_CANDIDATES[-1]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--pokecrystal", default=find_pokecrystal())
|
||||
parser.add_argument("--symbols", default=DEFAULT_SYMBOLS)
|
||||
parser.add_argument("--out", default=DEFAULT_OUT)
|
||||
args = parser.parse_args()
|
||||
|
||||
pokecrystal = os.path.abspath(args.pokecrystal)
|
||||
if not os.path.isfile(os.path.join(pokecrystal, "main.asm")):
|
||||
raise SystemExit(f"{pokecrystal} is not a pokecrystal checkout")
|
||||
data = generate(pokecrystal, os.path.abspath(args.symbols))
|
||||
with open(args.out, "w", encoding="utf-8", newline="\n") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2, sort_keys=True)
|
||||
f.write("\n")
|
||||
print(f"wrote {args.out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+155
-86
@@ -69,8 +69,14 @@ def strip_comment(line):
|
||||
return "".join(out).rstrip()
|
||||
|
||||
|
||||
def read_asm(path):
|
||||
"""Read an asm file as (lineno, text), comments stripped, IF resolved."""
|
||||
def read_asm(path, defines=None):
|
||||
"""Read an asm file as (lineno, text), comments stripped, IF resolved.
|
||||
|
||||
`defines` overrides ASM_DEFINES for another tree; pokecrystal's retail
|
||||
v1.0 target passes no -D at all (../pokecrystal/Makefile:129).
|
||||
"""
|
||||
if defines is None:
|
||||
defines = ASM_DEFINES
|
||||
lines = []
|
||||
stack = [] # stack of [taking, condition_known]
|
||||
with open(path, encoding="utf-8") as f:
|
||||
@@ -79,7 +85,7 @@ def read_asm(path):
|
||||
s = line.strip()
|
||||
m = re.match(r"IF\s+(!)?DEF\((\w+)\)\s*$", s, re.IGNORECASE)
|
||||
if m:
|
||||
defined = m.group(2) in ASM_DEFINES
|
||||
defined = m.group(2) in defines
|
||||
taking = (not defined) if m.group(1) else defined
|
||||
stack.append([taking, True])
|
||||
continue
|
||||
@@ -115,7 +121,7 @@ def parse_number(tok):
|
||||
return -val if neg else val
|
||||
|
||||
|
||||
def parse_const_block(path, stop_at=None):
|
||||
def parse_const_block(path, stop_at=None, defines=None):
|
||||
"""Parse a linear const_def/const/const_skip block into an ordered list.
|
||||
|
||||
Index i of the returned list is the constant's value (None for a gap);
|
||||
@@ -123,7 +129,7 @@ def parse_const_block(path, stop_at=None):
|
||||
"""
|
||||
names = []
|
||||
value = None
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
if not s:
|
||||
continue
|
||||
@@ -153,7 +159,7 @@ def parse_const_block(path, stop_at=None):
|
||||
return names
|
||||
|
||||
|
||||
def parse_const_block_at(path, first_const, stop_at=None):
|
||||
def parse_const_block_at(path, first_const, stop_at=None, defines=None):
|
||||
"""Parse the one const_def block whose first `const` is `first_const`.
|
||||
|
||||
parse_const_block walks a file linearly and a second `const_def` in the
|
||||
@@ -166,7 +172,7 @@ def parse_const_block_at(path, first_const, stop_at=None):
|
||||
names = []
|
||||
value = None
|
||||
started = False
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
if not s:
|
||||
continue
|
||||
@@ -201,7 +207,7 @@ def parse_const_block_at(path, first_const, stop_at=None):
|
||||
return names
|
||||
|
||||
|
||||
def parse_prefixed_consts(path, prefixes, exact=()):
|
||||
def parse_prefixed_consts(path, prefixes, exact=(), defines=None):
|
||||
"""Ordered const names from a file, filtered by prefix (mixed blocks).
|
||||
|
||||
map_data_constants.asm and friends stack several unrelated const_def
|
||||
@@ -210,7 +216,7 @@ def parse_prefixed_consts(path, prefixes, exact=()):
|
||||
even when they carry no shared prefix (TOWN, BALL, ...).
|
||||
"""
|
||||
out = []
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
m = re.match(r"const\s+(\w+)", line.strip())
|
||||
if not m:
|
||||
continue
|
||||
@@ -220,7 +226,7 @@ def parse_prefixed_consts(path, prefixes, exact=()):
|
||||
return out
|
||||
|
||||
|
||||
def parse_sparse_consts(path, prefixes):
|
||||
def parse_sparse_consts(path, prefixes, defines=None):
|
||||
"""Ordered const names where index IS the const value (sparse blocks).
|
||||
|
||||
parse_prefixed_consts packs a block densely, which is wrong for a block
|
||||
@@ -233,7 +239,7 @@ def parse_sparse_consts(path, prefixes):
|
||||
"""
|
||||
out = []
|
||||
value = None
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
m = re.match(r"const_def(?:\s+(\d+))?\s*$", s)
|
||||
if m:
|
||||
@@ -259,7 +265,7 @@ def parse_sparse_consts(path, prefixes):
|
||||
return out
|
||||
|
||||
|
||||
def extract_trainer_classes(pokegold):
|
||||
def extract_trainer_classes(pokegold, defines=None):
|
||||
"""Ordered trainer class names (constants/trainer_constants.asm).
|
||||
|
||||
`trainerclass NAME` bumps its own counter and resets the const_def used
|
||||
@@ -271,7 +277,7 @@ def extract_trainer_classes(pokegold):
|
||||
classes = []
|
||||
members = {}
|
||||
current = None
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
m = re.match(r"trainerclass\s+(\w+)", s)
|
||||
if m:
|
||||
@@ -287,7 +293,7 @@ def extract_trainer_classes(pokegold):
|
||||
return classes, members
|
||||
|
||||
|
||||
def extract_items(pokegold):
|
||||
def extract_items(pokegold, defines=None):
|
||||
"""Ordered item ids, and how many of them ItemNames actually covers.
|
||||
|
||||
parse_const_block cannot do this file: after NUM_ITEMS the TM and HM items
|
||||
@@ -304,7 +310,7 @@ def extract_items(pokegold):
|
||||
order = []
|
||||
name_count = None
|
||||
in_macro = False
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
# The add_tm / add_hm macro bodies contain `const TM_\1` themselves;
|
||||
# counting those would insert a phantom "TM_" item before the real run.
|
||||
@@ -340,7 +346,7 @@ def extract_items(pokegold):
|
||||
return order[1:], name_count - 1
|
||||
|
||||
|
||||
def extract_specials(pokegold):
|
||||
def extract_specials(pokegold, defines=None):
|
||||
"""Ordered SpecialsPointers labels (data/events/special_pointers.asm).
|
||||
|
||||
The `special` script command carries an index into this table, not a name,
|
||||
@@ -350,7 +356,7 @@ def extract_specials(pokegold):
|
||||
path = os.path.join(pokegold, "data/events/special_pointers.asm")
|
||||
names = []
|
||||
in_macro = False
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
if re.match(r"MACRO\b", s):
|
||||
in_macro = True
|
||||
@@ -368,11 +374,11 @@ def extract_specials(pokegold):
|
||||
return names
|
||||
|
||||
|
||||
def extract_std_scripts(pokegold):
|
||||
def extract_std_scripts(pokegold, defines=None):
|
||||
"""Ordered StdScripts labels (engine/events/std_scripts.asm)."""
|
||||
path = os.path.join(pokegold, "engine/events/std_scripts.asm")
|
||||
names = []
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
m = re.match(r"add_stdscript\s+(\w+)", line.strip())
|
||||
if m:
|
||||
names.append(m.group(1))
|
||||
@@ -381,14 +387,14 @@ def extract_std_scripts(pokegold):
|
||||
return names
|
||||
|
||||
|
||||
def extract_map_groups(pokegold):
|
||||
def extract_map_groups(pokegold, defines=None):
|
||||
"""Parse constants/map_constants.asm's newgroup/map_const/endgroup."""
|
||||
path = os.path.join(pokegold, "constants/map_constants.asm")
|
||||
order = []
|
||||
groups = {}
|
||||
group = 0
|
||||
map_index = 0
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
if re.match(r"newgroup\s+\w+", s):
|
||||
group += 1
|
||||
@@ -411,12 +417,12 @@ def extract_map_groups(pokegold):
|
||||
return order, groups
|
||||
|
||||
|
||||
def extract_types(pokegold):
|
||||
def extract_types(pokegold, defines=None):
|
||||
"""Type constants are physical IDs, a gap, then special IDs (Gen 1-style)."""
|
||||
path = os.path.join(pokegold, "constants/type_constants.asm")
|
||||
types = {}
|
||||
value = None
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
m = re.match(r"const_def(?:\s+(\$?\w+))?$", s)
|
||||
if m:
|
||||
@@ -435,12 +441,14 @@ def extract_types(pokegold):
|
||||
return types
|
||||
|
||||
|
||||
def charmap(pokegold):
|
||||
def charmap(pokegold, defines=None):
|
||||
"""Byte -> text charmap, same shape as make_rom_manifest.charmap."""
|
||||
expansions = {
|
||||
"<DOT>": ".",
|
||||
"<LV>": "{LV}",
|
||||
"<ID>": "{ID}",
|
||||
# ../pokecrystal/constants/charmap.asm:6 -- $14 is "<PLAYER>" in English
|
||||
"<PLAY_G>": "<PLAYER>",
|
||||
# Compression bytes: the cart stores one byte and PlaceString expands
|
||||
# it into several glyphs. The font sheet only starts at $60, so these
|
||||
# three have no tile of their own and MUST be expanded here or the
|
||||
@@ -453,10 +461,15 @@ def charmap(pokegold):
|
||||
}
|
||||
out = {}
|
||||
path = os.path.join(pokegold, "constants/charmap.asm")
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
stripped = line.strip()
|
||||
# ../pokecrystal/constants/charmap.asm:423,433 -- the unown and ascii
|
||||
# charmaps are separate RGBDS charmaps, not more rows of the main one.
|
||||
if re.match(r"(pushc|newcharmap)\b", stripped):
|
||||
break
|
||||
m = re.match(
|
||||
r'charmap\s+"((?:[^"\\]|\\.)*)",\s*(\$[0-9a-fA-F]+)',
|
||||
line.strip())
|
||||
stripped)
|
||||
if not m:
|
||||
continue
|
||||
value = int(m.group(2)[1:], 16)
|
||||
@@ -478,22 +491,22 @@ def species_label(species_id):
|
||||
return "".join(part.capitalize() for part in parts)
|
||||
|
||||
|
||||
def pokemon_names(pokegold):
|
||||
def pokemon_names(pokegold, defines=None):
|
||||
"""PokemonNames dname entries, in declared (dex) order."""
|
||||
names = []
|
||||
path = os.path.join(pokegold, "data/pokemon/names.asm")
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
m = re.match(r'dname\s+"([^"]*)"', line.strip())
|
||||
if m:
|
||||
names.append(m.group(1))
|
||||
return names
|
||||
|
||||
|
||||
def music_order(pokegold):
|
||||
def music_order(pokegold, defines=None):
|
||||
"""Music_* labels in MUSIC_* id order from audio/music_pointers.asm."""
|
||||
path = os.path.join(pokegold, "audio/music_pointers.asm")
|
||||
order = []
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
m = re.match(r"dba\s+(Music_\w+)\s*$", line.strip())
|
||||
if m:
|
||||
order.append(m.group(1))
|
||||
@@ -502,11 +515,11 @@ def music_order(pokegold):
|
||||
return order
|
||||
|
||||
|
||||
def sfx_order(pokegold):
|
||||
def sfx_order(pokegold, defines=None):
|
||||
"""Sfx_* labels in SFX_* id order from audio/sfx_pointers.asm."""
|
||||
path = os.path.join(pokegold, "audio/sfx_pointers.asm")
|
||||
order = []
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
m = re.match(r"dba\s+(Sfx_\w+)\s*$", line.strip())
|
||||
if m:
|
||||
order.append(m.group(1))
|
||||
@@ -515,7 +528,27 @@ def sfx_order(pokegold):
|
||||
return order
|
||||
|
||||
|
||||
def pokemon_assets(pokegold, species_order, symbols):
|
||||
# ../pokecrystal/main.asm:425-448 -- the four per-species pic-animation
|
||||
# tables; field name in the manifest, symbol suffix in pokecrystal.sym.
|
||||
ANIM_LABEL_SUFFIXES = (
|
||||
("animLabel", "Animation"),
|
||||
("idleLabel", "AnimationIdle"),
|
||||
("bitmaskLabel", "Bitmasks"),
|
||||
("framesLabel", "Frames"),
|
||||
)
|
||||
|
||||
|
||||
def _anim_labels(base, symbols):
|
||||
"""{animLabel/idleLabel/bitmaskLabel/framesLabel: label or None}."""
|
||||
out = {}
|
||||
for field, suffix in ANIM_LABEL_SUFFIXES:
|
||||
label = base + suffix
|
||||
out[field] = label if label in symbols.by_name else None
|
||||
return out
|
||||
|
||||
|
||||
def pokemon_assets(pokegold, species_order, symbols, defines=None,
|
||||
anim_labels=False):
|
||||
"""{species: {id, name, front, back, frontLabel, backLabel}}.
|
||||
|
||||
Unown shares one pic per letter through UnownPicPointers (its
|
||||
@@ -523,11 +556,15 @@ def pokemon_assets(pokegold, species_order, symbols):
|
||||
per-species Frontpic/Backpic label, so its front/back/labels are left
|
||||
null; the extractor's own Unown handling (if any) goes through
|
||||
UnownPicPointers directly instead of this table.
|
||||
|
||||
`anim_labels` adds the four pic-animation labels per species; off by
|
||||
default because pokegold.sym carries none of them.
|
||||
"""
|
||||
names = pokemon_names(pokegold)
|
||||
names = pokemon_names(pokegold, defines)
|
||||
assets = {}
|
||||
for index, species in enumerate(species_order):
|
||||
name = names[index] if index < len(names) else species
|
||||
base = species_label(species)
|
||||
if species == "UNOWN":
|
||||
# No per-species pic, but it does have a dex entry like anything
|
||||
# else, so the #DEX screen can still read it.
|
||||
@@ -537,8 +574,9 @@ def pokemon_assets(pokegold, species_order, symbols):
|
||||
"frontLabel": None, "backLabel": None,
|
||||
"dexLabel": "UnownPokedexEntry",
|
||||
}
|
||||
if anim_labels:
|
||||
assets[species].update(_anim_labels(base, symbols))
|
||||
continue
|
||||
base = species_label(species)
|
||||
front_label, back_label = base + "Frontpic", base + "Backpic"
|
||||
if front_label not in symbols.by_name or back_label not in symbols.by_name:
|
||||
raise ValueError(
|
||||
@@ -554,6 +592,8 @@ def pokemon_assets(pokegold, species_order, symbols):
|
||||
"frontLabel": front_label, "backLabel": back_label,
|
||||
"dexLabel": dex_label if dex_label in symbols.by_name else None,
|
||||
}
|
||||
if anim_labels:
|
||||
assets[species].update(_anim_labels(base, symbols))
|
||||
return assets
|
||||
|
||||
|
||||
@@ -800,6 +840,9 @@ REQUIRED_SYMBOLS = {
|
||||
# who just spotted the player, and the other faces scripts use.
|
||||
"ShockEmote", "QuestionEmote", "HappyEmote", "SadEmote",
|
||||
"HeartEmote", "BoltEmote", "SleepEmote", "FishEmote",
|
||||
# gfx/overworld/chris_fish.2bpp (engine/events/fishing_gfx.asm:23): the
|
||||
# fishing pose rows and the rod tiles FacingFish* parks by the player.
|
||||
"FishingGFX",
|
||||
# The Pokecenter heal machine's OBJ art (engine/events/
|
||||
# heal_machine_anim.asm): two tiles -- the machine's light ($7c) and the
|
||||
# ball ($7d) -- plus the CGB palette .LoadPalettes copies over
|
||||
@@ -927,7 +970,7 @@ TEXT_SOURCES = (
|
||||
)
|
||||
|
||||
|
||||
def text_labels(pokegold):
|
||||
def text_labels(pokegold, defines=None):
|
||||
"""Every text label in TEXT_SOURCES, in sorted order.
|
||||
|
||||
Unlike Gen 1 there is no `dynamic` map beside this. pokered's decoder is
|
||||
@@ -939,7 +982,7 @@ def text_labels(pokegold):
|
||||
for name in TEXT_SOURCES:
|
||||
path = os.path.join(pokegold, "data/text", name)
|
||||
pending = None
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
@@ -959,9 +1002,11 @@ def text_labels(pokegold):
|
||||
|
||||
|
||||
def embedded_symbols(symbols, pokemon_labels, song_labels=(),
|
||||
text_label_names=()):
|
||||
text_label_names=(), required=None):
|
||||
"""Resolve REQUIRED_SYMBOLS + pic labels + songs + text labels."""
|
||||
names = (set(REQUIRED_SYMBOLS) | set(pokemon_labels)
|
||||
if required is None:
|
||||
required = REQUIRED_SYMBOLS
|
||||
names = (set(required) | set(pokemon_labels)
|
||||
| set(song_labels) | set(text_label_names))
|
||||
for symbol_name in symbols.by_name:
|
||||
# Pokedex entries are split across four banks and the game derives the
|
||||
@@ -985,23 +1030,24 @@ def embedded_symbols(symbols, pokemon_labels, song_labels=(),
|
||||
}
|
||||
|
||||
|
||||
def generate(pokegold, symbols_path):
|
||||
def generate(pokegold, symbols_path, defines=None, required=None,
|
||||
sha1=None, anim_labels=False):
|
||||
symbols = SymbolTable(symbols_path)
|
||||
|
||||
species = parse_const_block(
|
||||
os.path.join(pokegold, "constants/pokemon_constants.asm"),
|
||||
stop_at="NUM_POKEMON")
|
||||
stop_at="NUM_POKEMON", defines=defines)
|
||||
species_order = [n or "UNUSED" for n in species[1:]]
|
||||
|
||||
map_order, map_groups = extract_map_groups(pokegold)
|
||||
map_order, map_groups = extract_map_groups(pokegold, defines)
|
||||
|
||||
tilesets = [n for n in parse_const_block(
|
||||
os.path.join(pokegold, "constants/tileset_constants.asm"),
|
||||
stop_at="NUM_TILESETS") if n]
|
||||
stop_at="NUM_TILESETS", defines=defines) if n]
|
||||
|
||||
moves = parse_const_block(
|
||||
os.path.join(pokegold, "constants/move_constants.asm"),
|
||||
stop_at="NUM_ATTACKS")
|
||||
stop_at="NUM_ATTACKS", defines=defines)
|
||||
move_order = [n or "UNUSED" for n in moves[1:]]
|
||||
|
||||
# sprite_constants.asm stacks the ids of two different tables. $01..
|
||||
@@ -1019,26 +1065,29 @@ def generate(pokegold, symbols_path):
|
||||
# wVariableSprites, so naming them here would let World:resolveSprite hand
|
||||
# a slot id back as though it were something that could be drawn.
|
||||
sprite_path = os.path.join(pokegold, "constants/sprite_constants.asm")
|
||||
sprites = parse_const_block(sprite_path, stop_at="NUM_POKEMON_SPRITES")
|
||||
sprites = parse_const_block(
|
||||
sprite_path, stop_at="NUM_POKEMON_SPRITES", defines=defines)
|
||||
sprite_order = [n or "UNUSED" for n in sprites[1:]]
|
||||
# The two DEFs the block itself ends its halves on. The $60..$7f hole
|
||||
# between them stays in sprite_order as UNUSED rows so the ids line up.
|
||||
num_overworld_sprites = len(
|
||||
parse_const_block(sprite_path, stop_at="NUM_OVERWORLD_SPRITES")) - 1
|
||||
parse_const_block(
|
||||
sprite_path, stop_at="NUM_OVERWORLD_SPRITES",
|
||||
defines=defines)) - 1
|
||||
sprite_pokemon = sprites.index("SPRITE_UNOWN")
|
||||
if len(sprites) - sprite_pokemon != 35:
|
||||
raise SystemExit(
|
||||
f"{sprite_path}: expected 35 SpriteMons ids, got "
|
||||
f"{len(sprites) - sprite_pokemon}")
|
||||
|
||||
types = extract_types(pokegold)
|
||||
types = extract_types(pokegold, defines)
|
||||
|
||||
# Environment (1-based), palette (0-based), fish-group (0-based) name
|
||||
# tables -- scraped by prefix so the mixed const_def blocks in
|
||||
# map_data_constants.asm cannot collide with each other.
|
||||
environments, palettes, fish_groups, spawns = [], [], [], []
|
||||
path = os.path.join(pokegold, "constants/map_data_constants.asm")
|
||||
for _, line in read_asm(path):
|
||||
for _, line in read_asm(path, defines):
|
||||
s = line.strip()
|
||||
m = re.match(r"const\s+(\w+)", s)
|
||||
if not m:
|
||||
@@ -1060,7 +1109,8 @@ def generate(pokegold, symbols_path):
|
||||
# Moves' effect byte into one of these names so the battle engine can
|
||||
# switch on a readable id instead of a raw number.
|
||||
move_effects = parse_const_block(
|
||||
os.path.join(pokegold, "constants/move_effect_constants.asm"))
|
||||
os.path.join(pokegold, "constants/move_effect_constants.asm"),
|
||||
defines=defines)
|
||||
move_effect_order = [n or "EFFECT_UNUSED" for n in move_effects]
|
||||
|
||||
# Battle animations. constants/battle_anim_constants.asm stacks nine
|
||||
@@ -1070,55 +1120,66 @@ def generate(pokegold, symbols_path):
|
||||
# framesets, OAM sets, GFX sheets) plus the BG-effect and palette enums a
|
||||
# disassembled animation refers to by number.
|
||||
battle_anim = os.path.join(pokegold, "constants/battle_anim_constants.asm")
|
||||
battle_anim_objects = parse_prefixed_consts(battle_anim, ("BATTLE_ANIM_OBJ_",))
|
||||
battle_anim_funcs = parse_prefixed_consts(battle_anim, ("BATTLE_ANIM_FUNC_",))
|
||||
battle_anim_objects = parse_prefixed_consts(
|
||||
battle_anim, ("BATTLE_ANIM_OBJ_",), defines=defines)
|
||||
battle_anim_funcs = parse_prefixed_consts(
|
||||
battle_anim, ("BATTLE_ANIM_FUNC_",), defines=defines)
|
||||
battle_anim_framesets = parse_prefixed_consts(
|
||||
battle_anim, ("BATTLE_ANIM_FRAMESET_",))
|
||||
battle_anim_oamsets = parse_prefixed_consts(battle_anim, ("BATTLE_ANIM_OAMSET_",))
|
||||
battle_anim, ("BATTLE_ANIM_FRAMESET_",), defines=defines)
|
||||
battle_anim_oamsets = parse_prefixed_consts(
|
||||
battle_anim, ("BATTLE_ANIM_OAMSET_",), defines=defines)
|
||||
# BATTLE_ANIM_GFX_* is the one block here that does not start at zero
|
||||
# (`const_def 1`, because AnimObjGFX row 0 is the empty AnimObj00GFX).
|
||||
# A placeholder in front keeps every list in this group indexable as
|
||||
# value + 1, so the extractor never has to remember which is which.
|
||||
battle_anim_gfx = ["BATTLE_ANIM_GFX_NONE"] + parse_prefixed_consts(
|
||||
battle_anim, ("BATTLE_ANIM_GFX_",))
|
||||
battle_bg_effects = parse_prefixed_consts(battle_anim, ("BATTLE_BG_EFFECT_",))
|
||||
battle_anim, ("BATTLE_ANIM_GFX_",), defines=defines)
|
||||
battle_bg_effects = parse_prefixed_consts(
|
||||
battle_anim, ("BATTLE_BG_EFFECT_",), defines=defines)
|
||||
# Two separate blocks, each starting at zero: a BG palette 4 and an OBJ
|
||||
# palette 4 are different colours, so they cannot share one list.
|
||||
battle_anim_bg_pals = parse_prefixed_consts(battle_anim, ("PAL_BATTLE_BG_",))
|
||||
battle_anim_ob_pals = parse_prefixed_consts(battle_anim, ("PAL_BATTLE_OB_",))
|
||||
battle_anim_bg_pals = parse_prefixed_consts(
|
||||
battle_anim, ("PAL_BATTLE_BG_",), defines=defines)
|
||||
battle_anim_ob_pals = parse_prefixed_consts(
|
||||
battle_anim, ("PAL_BATTLE_OB_",), defines=defines)
|
||||
|
||||
item_data = os.path.join(pokegold, "constants/item_data_constants.asm")
|
||||
# Pocket ids are 0-based (ITEM, KEY_ITEM, BALL, TM_HM); the ITEM_* /
|
||||
# ITEMMENU_* / HELD_* blocks share the file, hence the prefix scrape.
|
||||
pocket_order = parse_prefixed_consts(
|
||||
item_data, (), exact=("ITEM", "KEY_ITEM", "BALL", "TM_HM"))
|
||||
item_menu_order = parse_prefixed_consts(item_data, ("ITEMMENU_",))
|
||||
item_data, (), exact=("ITEM", "KEY_ITEM", "BALL", "TM_HM"),
|
||||
defines=defines)
|
||||
item_menu_order = parse_prefixed_consts(
|
||||
item_data, ("ITEMMENU_",), defines=defines)
|
||||
# The HELD_* block is sparse (const_skip / const_next), so the index of
|
||||
# this list must BE the ItemAttributes effect byte or every held effect
|
||||
# past HELD_CLEANSE_TAG lands on the wrong item.
|
||||
held_effect_order = parse_sparse_consts(item_data, ("HELD_",))
|
||||
held_effect_order = parse_sparse_consts(
|
||||
item_data, ("HELD_",), defines=defines)
|
||||
|
||||
mon_data = os.path.join(pokegold, "constants/pokemon_data_constants.asm")
|
||||
growth_order = parse_prefixed_consts(mon_data, ("GROWTH_",))
|
||||
egg_group_order = parse_prefixed_consts(mon_data, ("EGG_",))
|
||||
evolve_order = parse_prefixed_consts(mon_data, ("EVOLVE_",))
|
||||
growth_order = parse_prefixed_consts(mon_data, ("GROWTH_",), defines=defines)
|
||||
egg_group_order = parse_prefixed_consts(mon_data, ("EGG_",), defines=defines)
|
||||
evolve_order = parse_prefixed_consts(mon_data, ("EVOLVE_",), defines=defines)
|
||||
|
||||
# Map callbacks (constants/map_setup_constants.asm). That block is
|
||||
# `const_def 1`, so MAPCALLBACK_TILES is 1 and index 0 of this list is a
|
||||
# placeholder -- the same "value + 1" indexing BATTLE_ANIM_GFX_* uses.
|
||||
map_setup = os.path.join(pokegold, "constants/map_setup_constants.asm")
|
||||
map_callback_order = ["MAPCALLBACK_NONE"] + parse_prefixed_consts(
|
||||
map_setup, ("MAPCALLBACK_",))
|
||||
map_setup, ("MAPCALLBACK_",), defines=defines)
|
||||
|
||||
# constants/script_constants.asm stacks a dozen unrelated blocks, so each
|
||||
# of these is found by the name its own block opens with.
|
||||
script_consts = os.path.join(pokegold, "constants/script_constants.asm")
|
||||
cmd_queue_order = parse_const_block_at(
|
||||
script_consts, "CMDQUEUE_NULL", stop_at="NUM_CMDQUEUE_TYPES")
|
||||
script_consts, "CMDQUEUE_NULL", stop_at="NUM_CMDQUEUE_TYPES",
|
||||
defines=defines)
|
||||
floor_order = parse_const_block_at(
|
||||
script_consts, "FLOOR_B4F", stop_at="NUM_FLOORS")
|
||||
script_consts, "FLOOR_B4F", stop_at="NUM_FLOORS", defines=defines)
|
||||
deco_desc_order = parse_const_block_at(
|
||||
script_consts, "DECODESC_POSTER", stop_at="NUM_DECODESCS")
|
||||
script_consts, "DECODESC_POSTER", stop_at="NUM_DECODESCS",
|
||||
defines=defines)
|
||||
|
||||
# The phone. PHONE_* doubles as the PhoneContacts row index and carries
|
||||
# four const_skip holes that are real rows (the wrong-number fillers), so
|
||||
@@ -1129,33 +1190,41 @@ def generate(pokegold, symbols_path):
|
||||
# every contact past a hole four rows down its own table.
|
||||
phone_contact_order = [
|
||||
n or "PHONE_UNUSED" for n in parse_const_block_at(
|
||||
phone_consts, "PHONE_00", stop_at="NUM_PHONE_CONTACTS")]
|
||||
phone_consts, "PHONE_00", stop_at="NUM_PHONE_CONTACTS",
|
||||
defines=defines)]
|
||||
special_call_order = parse_const_block_at(
|
||||
phone_consts, "SPECIALCALL_NONE", stop_at="NUM_SPECIALCALLS")
|
||||
phone_consts, "SPECIALCALL_NONE", stop_at="NUM_SPECIALCALLS",
|
||||
defines=defines)
|
||||
|
||||
npc_trade = os.path.join(pokegold, "constants/npc_trade_constants.asm")
|
||||
trade_gender_order = parse_prefixed_consts(npc_trade, ("TRADE_GENDER_",))
|
||||
trade_dialog_order = parse_prefixed_consts(npc_trade, ("TRADE_DIALOGSET_",))
|
||||
trade_gender_order = parse_prefixed_consts(
|
||||
npc_trade, ("TRADE_GENDER_",), defines=defines)
|
||||
trade_dialog_order = parse_prefixed_consts(
|
||||
npc_trade, ("TRADE_DIALOGSET_",), defines=defines)
|
||||
|
||||
trainer_classes, trainer_members = extract_trainer_classes(pokegold)
|
||||
trainer_classes, trainer_members = extract_trainer_classes(
|
||||
pokegold, defines)
|
||||
trainer_types = parse_prefixed_consts(
|
||||
os.path.join(pokegold, "constants/trainer_data_constants.asm"),
|
||||
("TRAINERTYPE_",))
|
||||
("TRAINERTYPE_",), defines=defines)
|
||||
|
||||
landmarks = parse_const_block(
|
||||
os.path.join(pokegold, "constants/landmark_constants.asm"),
|
||||
stop_at="NUM_LANDMARKS")
|
||||
stop_at="NUM_LANDMARKS", defines=defines)
|
||||
landmark_order = [n or "UNUSED" for n in landmarks]
|
||||
|
||||
icons = parse_prefixed_consts(
|
||||
os.path.join(pokegold, "constants/icon_constants.asm"), ("ICON_",))
|
||||
os.path.join(pokegold, "constants/icon_constants.asm"), ("ICON_",),
|
||||
defines=defines)
|
||||
|
||||
tree_sets = parse_prefixed_consts(mon_data, ("TREEMON_SET_",))
|
||||
tree_sets = parse_prefixed_consts(
|
||||
mon_data, ("TREEMON_SET_",), defines=defines)
|
||||
|
||||
std_scripts = extract_std_scripts(pokegold)
|
||||
specials = extract_specials(pokegold)
|
||||
std_scripts = extract_std_scripts(pokegold, defines)
|
||||
specials = extract_specials(pokegold, defines)
|
||||
|
||||
assets = pokemon_assets(pokegold, species_order, symbols)
|
||||
assets = pokemon_assets(pokegold, species_order, symbols, defines,
|
||||
anim_labels=anim_labels)
|
||||
pokemon_labels = []
|
||||
for asset in assets.values():
|
||||
if asset["frontLabel"]:
|
||||
@@ -1163,19 +1232,19 @@ def generate(pokegold, symbols_path):
|
||||
if asset["backLabel"]:
|
||||
pokemon_labels.append(asset["backLabel"])
|
||||
|
||||
songs = music_order(pokegold)
|
||||
text_label_names = text_labels(pokegold)
|
||||
sfx = sfx_order(pokegold)
|
||||
songs = music_order(pokegold, defines)
|
||||
text_label_names = text_labels(pokegold, defines)
|
||||
sfx = sfx_order(pokegold, defines)
|
||||
|
||||
# Index 0 is NO_ITEM, so the parsed list is already 1-based on item id.
|
||||
# ItemNames only has rows for the first `item_name_count` of them; the TM
|
||||
# and HM items past that are named from their TM number instead.
|
||||
item_order, item_name_count = extract_items(pokegold)
|
||||
item_order, item_name_count = extract_items(pokegold, defines)
|
||||
|
||||
data = {
|
||||
"format": 3,
|
||||
"generation": 2,
|
||||
"romSha1": CANONICAL_GOLD_SHA1,
|
||||
"romSha1": sha1 or CANONICAL_GOLD_SHA1,
|
||||
"constants": {
|
||||
"source": "pret/pokegold constants/*.asm",
|
||||
"speciesOrder": species_order,
|
||||
@@ -1259,7 +1328,7 @@ def generate(pokegold, symbols_path):
|
||||
# Label -> decoded string is built at import time from these, the
|
||||
# same way Gen 1 builds data/generated/text.lua from its own list.
|
||||
"text": {"labels": text_label_names},
|
||||
"charmap": charmap(pokegold),
|
||||
"charmap": charmap(pokegold, defines),
|
||||
"fontCharmap": font_extract.parse_charmap(pokegold),
|
||||
"pokemonAssets": assets,
|
||||
# Per-map metadata (group/map/width/height/name). RomExtractorGen2
|
||||
@@ -1268,7 +1337,7 @@ def generate(pokegold, symbols_path):
|
||||
"tilesets": {name: {} for name in tilesets},
|
||||
}
|
||||
data["symbols"] = embedded_symbols(
|
||||
symbols, pokemon_labels, songs, text_label_names)
|
||||
symbols, pokemon_labels, songs, text_label_names, required=required)
|
||||
return data
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -741,7 +741,7 @@ def imported_data_dir(repo):
|
||||
return path if _generated_data_dir_ok(path) else None
|
||||
|
||||
version = (os.environ.get("POKEPORT_VERSION") or "red").lower()
|
||||
if version not in ("red", "blue", "yellow", "gold", "silver"):
|
||||
if version not in ("red", "blue", "yellow", "gold", "silver", "crystal"):
|
||||
version = "red"
|
||||
|
||||
candidates = [
|
||||
@@ -2404,8 +2404,8 @@ UPVALUE_CALL = re.compile(
|
||||
UPVALUE_ARGS = re.compile(
|
||||
r"""^\s*([A-Za-z_]\w*)\s*\.\s*(\w+)\s*,\s*["'](\w+)["']""")
|
||||
VERSION_MATCH = re.compile(
|
||||
r"""[=~]=\s*["'](red|blue|yellow|gold|silver)["']"""
|
||||
r"""|["'](red|blue|yellow|gold|silver)["']\s*[=~]=""")
|
||||
r"""[=~]=\s*["'](red|blue|yellow|gold|silver|crystal)["']"""
|
||||
r"""|["'](red|blue|yellow|gold|silver|crystal)["']\s*[=~]=""")
|
||||
|
||||
|
||||
def _line_of(body, offset):
|
||||
|
||||
@@ -14,6 +14,9 @@ CANONICAL_YELLOW_SHA1 = "cc7d03262ebfaf2f06772c1a480c7d9d5f4a38e1"
|
||||
# Gold and Silver are Gen 2: 2 MiB carts, twice the size of the Gen 1 ROMs above.
|
||||
CANONICAL_GOLD_SHA1 = "d8b8a3600a465308c9953dfa04f0081c05bdcb94"
|
||||
CANONICAL_SILVER_SHA1 = "49b163f7e57702bc939d642a18f591de55d92dae"
|
||||
# Crystal is the retail international v1.0 build (pret/pokecrystal's default
|
||||
# `make` target), also 2 MiB.
|
||||
CANONICAL_CRYSTAL_SHA1 = "f4cd194bdee0d04ca4eac29e09b8e4e9d818c133"
|
||||
ROM_BANK_SIZE = 0x4000
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14072,6 +14072,10 @@
|
||||
36,
|
||||
27127
|
||||
],
|
||||
"FishingGFX": [
|
||||
20,
|
||||
17792
|
||||
],
|
||||
"FlaaffyBackpic": [
|
||||
25,
|
||||
23889
|
||||
|
||||
@@ -14072,6 +14072,10 @@
|
||||
36,
|
||||
27127
|
||||
],
|
||||
"FishingGFX": [
|
||||
20,
|
||||
17792
|
||||
],
|
||||
"FlaaffyBackpic": [
|
||||
25,
|
||||
24656
|
||||
|
||||
@@ -103,6 +103,9 @@ local function applyLoaded(path, statusVerb)
|
||||
S.loadError = false
|
||||
S.allowSave = true
|
||||
end
|
||||
if Gen.of(S.save, S.version) == 2 then
|
||||
S.events = Catalog.gen2EventList(Gen.engineOf(S.save, S.version), S.modRoots)
|
||||
end
|
||||
local mapId = Gen.playerMap(S.save)
|
||||
S.mapId = mapId
|
||||
S.dirty = false
|
||||
@@ -169,16 +172,14 @@ function App.load(pathOverride, opts)
|
||||
for _, mod in ipairs(S.mods:status().loaded) do
|
||||
modRoots[#modRoots + 1] = mod.path
|
||||
end
|
||||
S.modRoots = modRoots
|
||||
if Gen.of(nil, opts.version) == 2 or require("src.core.GameVersion").generation() == 2 then
|
||||
S.events = Catalog.goldEventList(modRoots)
|
||||
S.events = Catalog.gen2EventList(Gen.engineOf(nil, opts.version), modRoots)
|
||||
else
|
||||
S.events = Catalog.scrapeEvents("data/scripts", "data/generated/trainer_headers.lua",
|
||||
nil, modRoots)
|
||||
end
|
||||
applyLoaded(pathOverride or SaveIO.defaultPath(), "Loaded")
|
||||
if Gen.of(S.save, S.version) == 2 then
|
||||
S.events = Catalog.goldEventList(modRoots)
|
||||
end
|
||||
end
|
||||
|
||||
-- Switch to another save file (Open button, drag-drop, or --save arg).
|
||||
|
||||
@@ -125,15 +125,8 @@ function Catalog.scrapeEvents(scriptDir, headerPath, listFiles, extraDirs)
|
||||
return sortedKeys(found)
|
||||
end
|
||||
|
||||
function Catalog.goldEventList(extraDirs)
|
||||
local names = {}
|
||||
local ok, flags = pcall(require, "src.core.gen2.FlagNames")
|
||||
if ok and flags and flags.events then
|
||||
for name in pairs(flags.events) do
|
||||
names[#names + 1] = name
|
||||
end
|
||||
end
|
||||
table.sort(names)
|
||||
function Catalog.gen2EventList(engine, extraDirs)
|
||||
local names = require("Gen2Flags").names(engine)
|
||||
local modFlags = Catalog.scrapeEvents(nil, nil, nil, extraDirs)
|
||||
local seen = {}
|
||||
for _, name in ipairs(names) do seen[name] = true end
|
||||
@@ -146,4 +139,8 @@ function Catalog.goldEventList(extraDirs)
|
||||
return names
|
||||
end
|
||||
|
||||
function Catalog.goldEventList(extraDirs)
|
||||
return Catalog.gen2EventList("gs", extraDirs)
|
||||
end
|
||||
|
||||
return Catalog
|
||||
|
||||
@@ -24,6 +24,37 @@ function Gen.of(save, version)
|
||||
return GameVersion.generation()
|
||||
end
|
||||
|
||||
local function versionOf(save, version)
|
||||
if type(save) == "table" and GameVersion.VERSIONS[save.version] then
|
||||
return save.version
|
||||
end
|
||||
if type(version) == "string" and GameVersion.VERSIONS[version] then
|
||||
return version
|
||||
end
|
||||
return GameVersion.get()
|
||||
end
|
||||
Gen.versionOf = versionOf
|
||||
|
||||
function Gen.engineOf(save, version)
|
||||
return GameVersion.engine(versionOf(save, version))
|
||||
end
|
||||
|
||||
function Gen.editionLabel(save, version)
|
||||
local info = GameVersion.info(versionOf(save, version))
|
||||
return tostring((info and info.label) or "GEN 2"):upper()
|
||||
end
|
||||
|
||||
function Gen.hasCaughtData(save, version)
|
||||
if Gen.of(save, version) ~= 2 then return false end
|
||||
return require("src.battle.gen2.Mon").hasCaughtData(versionOf(save, version))
|
||||
end
|
||||
|
||||
-- engine/menus/init_gender.asm:23-38
|
||||
function Gen.hasPlayerGender(save, version)
|
||||
if Gen.of(save, version) ~= 2 then return false end
|
||||
return Gen.engineOf(save, version) == "crystal"
|
||||
end
|
||||
|
||||
function Gen.ofState(S)
|
||||
if not S then return GameVersion.generation() end
|
||||
return Gen.of(S.save, S.version)
|
||||
@@ -110,10 +141,17 @@ function Gen.bindGoldData(data)
|
||||
end
|
||||
|
||||
function Gen.newGame(version)
|
||||
if (versionGeneration(version) or GameVersion.generation(version)) == 2 then
|
||||
return require("src.core.gen2.Save").newGame()
|
||||
local id = type(version) == "string" and GameVersion.VERSIONS[version] and version
|
||||
or nil
|
||||
if (versionGeneration(id) or GameVersion.generation()) == 2 then
|
||||
local Save2 = require("src.core.gen2.Save")
|
||||
local save = Save2.newGame({
|
||||
playerName = id and Save2.defaultPlayerName(id) or nil,
|
||||
})
|
||||
if id then save.version = id end
|
||||
return save
|
||||
end
|
||||
return require("src.core.SaveData").newGame()
|
||||
return require("src.core.SaveData").newGame({ version = id })
|
||||
end
|
||||
|
||||
function Gen.validate(save, data)
|
||||
@@ -223,6 +261,34 @@ function Gen.setCoins(save, amount)
|
||||
end
|
||||
end
|
||||
|
||||
-- constants/ram_constants.asm:176-177
|
||||
function Gen.playerGender(save)
|
||||
local stored = save and save.player and save.player.gender
|
||||
return stored == "female" and "female" or "male"
|
||||
end
|
||||
|
||||
function Gen.setPlayerGender(save, gender)
|
||||
save.player = save.player or {}
|
||||
save.player.gender = (gender == "female") and "female" or "male"
|
||||
return save.player.gender
|
||||
end
|
||||
|
||||
-- constants/landmark_constants.asm:3, :111-113
|
||||
function Gen.landmarkName(data, index)
|
||||
index = math.floor(tonumber(index) or 0)
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
if index == Mon.LANDMARK_EVENT then return "EVENT" end
|
||||
if index == Mon.LANDMARK_GIFT then return "GIFT" end
|
||||
if index == 0 then return "UNKNOWN" end
|
||||
local rows = data and data.gen2Landmarks and data.gen2Landmarks.landmarks
|
||||
for _, rec in pairs(rows or {}) do
|
||||
if type(rec) == "table" and rec.index == index then
|
||||
return (tostring(rec.name or rec.id):gsub("\n", " "))
|
||||
end
|
||||
end
|
||||
return ("#%d"):format(index)
|
||||
end
|
||||
|
||||
function Gen.dexOwnedKey(save)
|
||||
if Gen.of(save) == 2 then return "caught" end
|
||||
return "owned"
|
||||
@@ -317,14 +383,13 @@ function Gen.toggleBadge(save, id)
|
||||
return not on
|
||||
end
|
||||
|
||||
local function goldFlagId(name)
|
||||
local flags = require("src.core.gen2.FlagNames")
|
||||
return flags.events and flags.events[name]
|
||||
local function gen2FlagId(save, name)
|
||||
return require("Gen2Flags").byName(Gen.engineOf(save))[name]
|
||||
end
|
||||
|
||||
function Gen.getFlag(save, name)
|
||||
if Gen.of(save) == 2 then
|
||||
local id = goldFlagId(name)
|
||||
local id = gen2FlagId(save, name)
|
||||
if id then
|
||||
local Events2 = require("src.world.gen2.Events")
|
||||
local ev = Events2.new()
|
||||
@@ -338,7 +403,7 @@ end
|
||||
|
||||
function Gen.setFlag(save, name, on)
|
||||
if Gen.of(save) == 2 then
|
||||
local id = goldFlagId(name)
|
||||
local id = gen2FlagId(save, name)
|
||||
if id then
|
||||
local Events2 = require("src.world.gen2.Events")
|
||||
local ev = Events2.new()
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
-- ../pokecrystal/constants/event_flags.asm:1 against
|
||||
-- ../pokegold/constants/event_flags.asm:1
|
||||
|
||||
local Gen2Flags = {}
|
||||
|
||||
Gen2Flags.CRYSTAL_ADDED = {
|
||||
EVENT_ALAN_GAVE_FIRE_STONE = 257,
|
||||
EVENT_ANSWERED_DRAGON_MASTER_QUIZ_WRONG = 193,
|
||||
EVENT_AZALEA_TOWN_KURT = 1956,
|
||||
EVENT_BATTLE_TOWER_BATTLE_ROOM_YOUNGSTER = 1937,
|
||||
EVENT_BATTLE_TOWER_OPEN_CIVILIANS = 1999,
|
||||
EVENT_BEAT_BEAUTY_OLIVIA = 1473,
|
||||
EVENT_BEAT_BUG_CATCHER_WAYNE = 1472,
|
||||
EVENT_BEAT_CAMPER_QUENTIN = 1475,
|
||||
EVENT_BEAT_COOLTRAINERF_CARA = 1470,
|
||||
EVENT_BEAT_COOLTRAINERM_DARIN = 1469,
|
||||
EVENT_BEAT_FISHER_TULLY = 1108,
|
||||
EVENT_BEAT_FISHER_TULLY2 = 1119,
|
||||
EVENT_BEAT_FISHER_TULLY3 = 1120,
|
||||
EVENT_BEAT_POKEFANF_JAIME = 1474,
|
||||
EVENT_BEAT_POKEFANM_ALLAN = 1480,
|
||||
EVENT_BEAT_POKEFANM_REX = 1479,
|
||||
EVENT_BEAT_POKEMANIAC_MILLER = 1476,
|
||||
EVENT_BEAT_SAGE_GAKU = 1481,
|
||||
EVENT_BEAT_SAGE_KOJI = 1483,
|
||||
EVENT_BEAT_SAGE_MASA = 1482,
|
||||
EVENT_BEAT_SUPER_NERD_HUGH = 1477,
|
||||
EVENT_BEAT_SUPER_NERD_MARKUS = 1478,
|
||||
EVENT_BEAT_SUPER_NERD_STAN = 1409,
|
||||
EVENT_BEAT_TWINS_LEA_AND_PIA = 1471,
|
||||
EVENT_BUENA_OFFERED_HER_PHONE_NUMBER = 828,
|
||||
EVENT_BUENA_OFFERED_HER_PHONE_NUMBER_NO_BLUE_CARD = 670,
|
||||
EVENT_BUGGING_KURT_TOO_MUCH = 187,
|
||||
EVENT_BURNED_TOWER_1F_EUSINE = 1893,
|
||||
EVENT_BURNED_TOWER_1F_HIDDEN_ULTRA_BALL = 255,
|
||||
EVENT_BURNED_TOWER_1F_HP_UP = 1622,
|
||||
EVENT_BURNED_TOWER_MORTY = 1892,
|
||||
EVENT_CAN_GIVE_GS_BALL_TO_KURT = 190,
|
||||
EVENT_CIANWOOD_CITY_EUSINE = 1965,
|
||||
EVENT_COPYCAT_1 = 1774,
|
||||
EVENT_COPYCAT_2 = 1775,
|
||||
EVENT_DANA_GAVE_THUNDERSTONE = 258,
|
||||
EVENT_DARK_CAVE_VIOLET_ENTRANCE_DIRE_HIT = 1998,
|
||||
EVENT_DRAGONS_DEN_B1F_CALCIUM = 1983,
|
||||
EVENT_DRAGONS_DEN_B1F_MAX_ELIXER = 1984,
|
||||
EVENT_DRAGON_SHRINE_CLAIR = 1936,
|
||||
EVENT_ECRUTEAK_CITY_GRAMPS = 1961,
|
||||
EVENT_ECRUTEAK_GYM_GRAMPS = 1960,
|
||||
EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_WANDERING_SAGE = 1969,
|
||||
EVENT_ERIN_CALCIUM = 827,
|
||||
EVENT_EUSINE_IN_BURNED_TOWER = 1962,
|
||||
EVENT_FLORIA_AT_FLOWER_SHOP = 1896,
|
||||
EVENT_FLORIA_AT_SUDOWOODO = 1897,
|
||||
EVENT_FOREST_IS_RESTLESS = 192,
|
||||
EVENT_FOUGHT_EUSINE = 819,
|
||||
EVENT_FOUGHT_SUICUNE = 821,
|
||||
EVENT_GAVE_GS_BALL_TO_KURT = 191,
|
||||
EVENT_GINA_GAVE_LEAF_STONE = 256,
|
||||
EVENT_GOLDENROD_CITY_MOVE_TUTOR = 1898,
|
||||
EVENT_GOLDENROD_GAME_CORNER_MOVE_TUTOR = 1899,
|
||||
EVENT_GOLDENROD_SALE_OFF = 1776,
|
||||
EVENT_GOLDENROD_SALE_ON = 1777,
|
||||
EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_ULTRA_BALL = 1621,
|
||||
EVENT_GOT_CLEAR_BELL = 120,
|
||||
EVENT_GOT_DRATINI = 189,
|
||||
EVENT_GOT_GS_BALL_FROM_GOLDENROD_POKEMON_CENTER = 832,
|
||||
EVENT_GOT_ODD_EGG = 830,
|
||||
EVENT_GOT_RAINBOW_WING = 822,
|
||||
EVENT_HOLE_IN_BURNED_TOWER = 818,
|
||||
EVENT_HUEY_PROTEIN = 823,
|
||||
EVENT_ICE_PATH_1F_PROTEIN = 1982,
|
||||
EVENT_ILEX_FOREST_ANTIDOTE = 1978,
|
||||
EVENT_ILEX_FOREST_ETHER = 1979,
|
||||
EVENT_ILEX_FOREST_FARFETCHD = 1769,
|
||||
EVENT_ILEX_FOREST_KURT = 1957,
|
||||
EVENT_ILEX_FOREST_LASS = 1773,
|
||||
EVENT_ILEX_FOREST_X_ATTACK = 1977,
|
||||
EVENT_JOEY_HP_UP = 824,
|
||||
EVENT_KOJI_ALLOWS_YOU_PASSAGE_TO_TIN_TOWER = 820,
|
||||
EVENT_KURTS_HOUSE_GRANDDAUGHTER_1 = 1932,
|
||||
EVENT_KURTS_HOUSE_GRANDDAUGHTER_2 = 1933,
|
||||
EVENT_LAKE_OF_RAGE_ELIXER = 1605,
|
||||
EVENT_LAKE_OF_RAGE_ELIXIR_ON_STANDBY = 58,
|
||||
EVENT_MET_BUENA = 829,
|
||||
EVENT_MET_FLORIA = 185,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_IRON = 1992,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_MAX_POTION = 1958,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_NUGGET = 1959,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_ULTRA_BALL = 1993,
|
||||
EVENT_MOUNT_MORTAR_B1F_CARBOS = 1671,
|
||||
EVENT_MOUNT_MORTAR_B1F_FULL_RESTORE = 1994,
|
||||
EVENT_MOUNT_MORTAR_B1F_MAX_ETHER = 1995,
|
||||
EVENT_MOUNT_MORTAR_B1F_PP_UP = 1996,
|
||||
EVENT_OLIVINE_LIGHTHOUSE_5F_SUPER_REPEL = 1638,
|
||||
EVENT_PARRY_IRON = 826,
|
||||
EVENT_PICKED_UP_BERRY_FROM_KABUTO_ITEM_ROOM = 1944,
|
||||
EVENT_PICKED_UP_CHARCOAL_FROM_HO_OH_ITEM_ROOM = 1943,
|
||||
EVENT_PICKED_UP_ENERGYPOWDER_FROM_KABUTO_ITEM_ROOM = 1947,
|
||||
EVENT_PICKED_UP_ENERGY_ROOT_FROM_AERODACTYL_ITEM_ROOM = 1955,
|
||||
EVENT_PICKED_UP_GOLD_BERRY_FROM_AERODACTYL_ITEM_ROOM = 1952,
|
||||
EVENT_PICKED_UP_GOLD_BERRY_FROM_HO_OH_ITEM_ROOM = 1940,
|
||||
EVENT_PICKED_UP_HEAL_POWDER_FROM_AERODACTYL_ITEM_ROOM = 1954,
|
||||
EVENT_PICKED_UP_HEAL_POWDER_FROM_KABUTO_ITEM_ROOM = 1946,
|
||||
EVENT_PICKED_UP_MOON_STONE_FROM_AERODACTYL_ITEM_ROOM = 1953,
|
||||
EVENT_PICKED_UP_MYSTERYBERRY_FROM_HO_OH_ITEM_ROOM = 1941,
|
||||
EVENT_PICKED_UP_MYSTERYBERRY_FROM_OMANYTE_ITEM_ROOM = 1948,
|
||||
EVENT_PICKED_UP_MYSTIC_WATER_FROM_OMANYTE_ITEM_ROOM = 1949,
|
||||
EVENT_PICKED_UP_PSNCUREBERRY_FROM_KABUTO_ITEM_ROOM = 1945,
|
||||
EVENT_PICKED_UP_REVIVAL_HERB_FROM_HO_OH_ITEM_ROOM = 1942,
|
||||
EVENT_PICKED_UP_STARDUST_FROM_OMANYTE_ITEM_ROOM = 1950,
|
||||
EVENT_PICKED_UP_STAR_PIECE_FROM_OMANYTE_ITEM_ROOM = 1951,
|
||||
EVENT_PLAYERS_HOUSE_1F_NEIGHBOR = 1938,
|
||||
EVENT_PLAYERS_NEIGHBORS_HOUSE_NEIGHBOR = 1939,
|
||||
EVENT_RADIO_TOWER_5F_ULTRA_BALL = 1997,
|
||||
EVENT_RANG_CLEAR_BELL_1 = 1894,
|
||||
EVENT_RANG_CLEAR_BELL_2 = 1895,
|
||||
EVENT_ROUTE_30_ANTIDOTE = 1976,
|
||||
EVENT_ROUTE_31_POTION = 1710,
|
||||
EVENT_ROUTE_32_REPEL = 1713,
|
||||
EVENT_ROUTE_34_ILEX_FOREST_GATE_LASS = 1771,
|
||||
EVENT_ROUTE_34_ILEX_FOREST_GATE_TEACHER_BEHIND_COUNTER = 1770,
|
||||
EVENT_ROUTE_34_ILEX_FOREST_GATE_TEACHER_IN_WALKWAY = 1772,
|
||||
EVENT_ROUTE_34_NUGGET = 1980,
|
||||
EVENT_ROUTE_44_MAX_REPEL = 1981,
|
||||
EVENT_ROUTE_45_NUGGET = 1720,
|
||||
EVENT_ROUTE_46_X_SPEED = 1724,
|
||||
EVENT_RUINS_OF_ALPH_OUTSIDE_TOURIST_FISHER = 1934,
|
||||
EVENT_RUINS_OF_ALPH_OUTSIDE_TOURIST_YOUNGSTERS = 1935,
|
||||
EVENT_SAW_SUICUNE_AT_CIANWOOD_CITY = 1966,
|
||||
EVENT_SAW_SUICUNE_ON_ROUTE_36 = 1968,
|
||||
EVENT_SAW_SUICUNE_ON_ROUTE_42 = 1967,
|
||||
EVENT_SET_WHEN_FOUGHT_HO_OH = 1975,
|
||||
EVENT_SILVER_CAVE_ROOM_1_PROTEIN = 1690,
|
||||
EVENT_SILVER_CAVE_ROOM_1_ULTRA_BALL = 1985,
|
||||
EVENT_SILVER_CAVE_ROOM_2_CALCIUM = 1986,
|
||||
EVENT_SILVER_CAVE_ROOM_2_PP_UP = 1988,
|
||||
EVENT_SILVER_CAVE_ROOM_2_ULTRA_BALL = 1987,
|
||||
EVENT_SPROUT_TOWER_2F_X_ACCURACY = 1608,
|
||||
EVENT_TALKED_TO_FLORIA_AT_FLOWER_SHOP = 186,
|
||||
EVENT_TALKED_TO_RUINS_COWARD = 188,
|
||||
EVENT_TEAM_ROCKET_BASE_B1F_GUARD_SPEC = 1643,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_FULL_HEAL = 1647,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_PROTEIN = 1645,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_ULTRA_BALL = 1620,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_X_SPECIAL = 1646,
|
||||
EVENT_TIFFANY_GAVE_PINK_BOW = 260,
|
||||
EVENT_TIN_TOWER_1F_ENTEI = 1971,
|
||||
EVENT_TIN_TOWER_1F_EUSINE = 1973,
|
||||
EVENT_TIN_TOWER_1F_RAIKOU = 1972,
|
||||
EVENT_TIN_TOWER_1F_SUICUNE = 1970,
|
||||
EVENT_TIN_TOWER_1F_WISE_TRIO_1 = 1974,
|
||||
EVENT_TIN_TOWER_1F_WISE_TRIO_2 = 1989,
|
||||
EVENT_TIN_TOWER_4F_PP_UP = 1613,
|
||||
EVENT_TIN_TOWER_6F_MAX_POTION = 1990,
|
||||
EVENT_TIN_TOWER_9F_HP_UP = 1991,
|
||||
EVENT_TULLY_ASKED_FOR_PHONE_NUMBER = 655,
|
||||
EVENT_TULLY_GAVE_WATER_STONE = 259,
|
||||
EVENT_UNION_CAVE_1F_POTION = 1628,
|
||||
EVENT_UNION_CAVE_1F_X_ATTACK = 1627,
|
||||
EVENT_VANCE_CARBOS = 825,
|
||||
EVENT_VICTORY_ROAD_HP_UP = 1703,
|
||||
EVENT_WADE_HAS_BERRY = 811,
|
||||
EVENT_WADE_HAS_BITTER_BERRY = 814,
|
||||
EVENT_WADE_HAS_PRZCUREBERRY = 813,
|
||||
EVENT_WADE_HAS_PSNCUREBERRY = 812,
|
||||
EVENT_WALL_OPENED_IN_AERODACTYL_CHAMBER = 809,
|
||||
EVENT_WALL_OPENED_IN_HO_OH_CHAMBER = 806,
|
||||
EVENT_WALL_OPENED_IN_KABUTO_CHAMBER = 807,
|
||||
EVENT_WALL_OPENED_IN_OMANYTE_CHAMBER = 808,
|
||||
EVENT_WELCOMED_TO_POKECOM_CENTER = 810,
|
||||
EVENT_WHIRL_ISLAND_SW_ULTRA_BALL = 1680,
|
||||
EVENT_WILTON_HAS_GREAT_BALL = 816,
|
||||
EVENT_WILTON_HAS_POKE_BALL = 817,
|
||||
EVENT_WILTON_HAS_ULTRA_BALL = 815,
|
||||
EVENT_WISE_TRIOS_ROOM_WISE_TRIO_1 = 1963,
|
||||
EVENT_WISE_TRIOS_ROOM_WISE_TRIO_2 = 1964,
|
||||
}
|
||||
|
||||
Gen2Flags.CRYSTAL_REMOVED = {
|
||||
EVENT_ALAN_READY_FOR_REMATCH = true,
|
||||
EVENT_ANTHONY_READY_FOR_REMATCH = true,
|
||||
EVENT_ARNIE_READY_FOR_REMATCH = true,
|
||||
EVENT_BEAT_FISHER_CHRIS = true,
|
||||
EVENT_BEAT_FISHER_CHRIS2 = true,
|
||||
EVENT_BEAT_FISHER_CHRIS3 = true,
|
||||
EVENT_BEAT_SUPER_NERD_ERIC_UNUSED = true,
|
||||
EVENT_BETH_READY_FOR_REMATCH = true,
|
||||
EVENT_BEVERLY_READY_FOR_REMATCH = true,
|
||||
EVENT_BRENT_READY_FOR_REMATCH = true,
|
||||
EVENT_BURNED_TOWER_1F_BURN_HEAL = true,
|
||||
EVENT_BURNED_TOWER_1F_X_SPEED = true,
|
||||
EVENT_BURNED_TOWER_B1F_HIDDEN_BURN_HEAL = true,
|
||||
EVENT_BURNED_TOWER_B1F_HIDDEN_NUGGET = true,
|
||||
EVENT_BURNED_TOWER_B1F_HIDDEN_ULTRA_BALL = true,
|
||||
EVENT_BURNED_TOWER_B1F_HP_UP = true,
|
||||
EVENT_BURNED_TOWER_FIREBREATHER_DICK_ASHES = true,
|
||||
EVENT_BURNED_TOWER_FIREBREATHER_DICK_NORMAL = true,
|
||||
EVENT_CHAD_READY_FOR_REMATCH = true,
|
||||
EVENT_CHRIS_ASKED_FOR_PHONE_NUMBER = true,
|
||||
EVENT_CHRIS_READY_FOR_REMATCH = true,
|
||||
EVENT_DANA_READY_FOR_REMATCH = true,
|
||||
EVENT_DEREK_READY_FOR_REMATCH = true,
|
||||
EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_LEFT = true,
|
||||
EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_RIGHT = true,
|
||||
EVENT_ERIN_READY_FOR_REMATCH = true,
|
||||
EVENT_GAVEN_READY_FOR_REMATCH = true,
|
||||
EVENT_GINA_READY_FOR_REMATCH = true,
|
||||
EVENT_HUEY_READY_FOR_REMATCH = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_1 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_10 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_2 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_3 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_4 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_5 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_6 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_7 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_8 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_9 = true,
|
||||
EVENT_IRWIN_READY_FOR_REMATCH = true,
|
||||
EVENT_JACK_READY_FOR_REMATCH = true,
|
||||
EVENT_JOEY_READY_FOR_REMATCH = true,
|
||||
EVENT_JOSE_READY_FOR_REMATCH = true,
|
||||
EVENT_KENJI_READY_FOR_REMATCH = true,
|
||||
EVENT_LAKE_OF_RAGE_ETHER_ON_STANDBY = true,
|
||||
EVENT_LAKE_OF_RAGE_MAX_ETHER = true,
|
||||
EVENT_LIZ_READY_FOR_REMATCH = true,
|
||||
EVENT_MOUNT_MORTAR_B1F_FULL_HEAL = true,
|
||||
EVENT_OLIVINE_LIGHTHOUSE_5F_GREAT_BALL = true,
|
||||
EVENT_PARRY_READY_FOR_REMATCH = true,
|
||||
EVENT_RALPH_READY_FOR_REMATCH = true,
|
||||
EVENT_REENA_READY_FOR_REMATCH = true,
|
||||
EVENT_ROUTE_31_ANTIDOTE = true,
|
||||
EVENT_ROUTE_32_POTION = true,
|
||||
EVENT_ROUTE_45_X_SPECIAL = true,
|
||||
EVENT_ROUTE_46_DIRE_HIT = true,
|
||||
EVENT_SILVER_CAVE_ROOM_1_X_ACCURACY = true,
|
||||
EVENT_SPROUT_TOWER_2F_X_DEFEND = true,
|
||||
EVENT_TEAM_ROCKET_BASE_B1F_X_ACCURACY = true,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_DIRE_HIT = true,
|
||||
EVENT_TIFFANY_READY_FOR_REMATCH = true,
|
||||
EVENT_TIN_TOWER_4F_SUPER_POTION = true,
|
||||
EVENT_TODD_READY_FOR_REMATCH = true,
|
||||
EVENT_VANCE_READY_FOR_REMATCH = true,
|
||||
EVENT_VICTORY_ROAD_X_SPECIAL = true,
|
||||
EVENT_WADE_READY_FOR_REMATCH = true,
|
||||
EVENT_WHIRL_ISLAND_SW_GUARD_SPEC = true,
|
||||
EVENT_WILTON_READY_FOR_REMATCH = true,
|
||||
}
|
||||
|
||||
local cache = {}
|
||||
|
||||
function Gen2Flags.byName(engine)
|
||||
engine = (engine == "crystal") and "crystal" or "gs"
|
||||
if cache[engine] then return cache[engine] end
|
||||
local ids = {}
|
||||
local ok, flags = pcall(require, "src.core.gen2.FlagNames")
|
||||
if ok and type(flags) == "table" and type(flags.events) == "table" then
|
||||
for name, id in pairs(flags.events) do ids[name] = id end
|
||||
end
|
||||
if engine == "crystal" then
|
||||
for name in pairs(Gen2Flags.CRYSTAL_REMOVED) do ids[name] = nil end
|
||||
for name, id in pairs(Gen2Flags.CRYSTAL_ADDED) do ids[name] = id end
|
||||
end
|
||||
cache[engine] = ids
|
||||
return ids
|
||||
end
|
||||
|
||||
function Gen2Flags.names(engine)
|
||||
local names = {}
|
||||
for name in pairs(Gen2Flags.byName(engine)) do names[#names + 1] = name end
|
||||
table.sort(names)
|
||||
return names
|
||||
end
|
||||
|
||||
return Gen2Flags
|
||||
@@ -1141,4 +1141,74 @@ function Ops.setPokerus(S, mon, value)
|
||||
return Ops.mark(S, ("%s pokerus byte %d"):format(mon.species, want))
|
||||
end
|
||||
|
||||
-- engine/pokemon/caught_data.asm:169-172
|
||||
Ops.CAUGHT_TIMES = { "UNKNOWN", "MORN", "DAY", "NITE" }
|
||||
|
||||
local function caughtGuard(S, mon)
|
||||
if not mon then return false end
|
||||
if not Gen.hasCaughtData(S.save, S.version) then
|
||||
return Ops.say(S, "This game has no caught data")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Ops.setCaughtTime(S, mon, value)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local want = clamp(math.floor(tonumber(value) or 0), 0, 3)
|
||||
if want == (mon.caughtTime or 0) then
|
||||
return Ops.say(S, ("Caught time is already %s"):format(Ops.CAUGHT_TIMES[want + 1]))
|
||||
end
|
||||
mon.caughtTime = want
|
||||
return Ops.mark(S, ("%s caught time %s")
|
||||
:format(mon.species, Ops.CAUGHT_TIMES[want + 1]))
|
||||
end
|
||||
|
||||
-- constants/pokemon_data_constants.asm:120-121
|
||||
function Ops.setCaughtLevel(S, mon, value)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
local want = clamp(math.floor(tonumber(value) or 0), 0, Mon.CAUGHT_LEVEL_MASK)
|
||||
if want == (mon.caughtLevel or 0) then
|
||||
return Ops.say(S, ("Caught level is already %d"):format(want))
|
||||
end
|
||||
mon.caughtLevel = want
|
||||
return Ops.mark(S, ("%s caught level %d"):format(mon.species, want))
|
||||
end
|
||||
|
||||
-- constants/landmark_constants.asm:111-113
|
||||
function Ops.setCaughtLocation(S, mon, value)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
local span = Mon.CAUGHT_LOCATION_MASK + 1
|
||||
local want = math.floor(tonumber(value) or 0) % span
|
||||
if want == (mon.caughtLocation or 0) then
|
||||
return Ops.say(S, ("Caught location is already %s")
|
||||
:format(Gen.landmarkName(S.data, want)))
|
||||
end
|
||||
mon.caughtLocation = want
|
||||
return Ops.mark(S, ("%s caught at %s")
|
||||
:format(mon.species, Gen.landmarkName(S.data, want)))
|
||||
end
|
||||
|
||||
function Ops.setCaughtByGender(S, mon, gender)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
local want = Mon.caughtGenderOf(gender)
|
||||
if want == mon.caughtByGender then
|
||||
return Ops.say(S, ("Caught by is already %s"):format(tostring(want or "none")))
|
||||
end
|
||||
mon.caughtByGender = want
|
||||
return Ops.mark(S, ("%s caught by %s"):format(mon.species, tostring(want or "none")))
|
||||
end
|
||||
|
||||
function Ops.setPlayerGender(S, gender)
|
||||
if not Gen.hasPlayerGender(S.save, S.version) then
|
||||
return Ops.say(S, "This game has no player gender")
|
||||
end
|
||||
if Gen.playerGender(S.save) == gender then
|
||||
return Ops.say(S, ("Player is already %s"):format(tostring(gender)))
|
||||
end
|
||||
return Ops.mark(S, ("Player gender %s"):format(Gen.setPlayerGender(S.save, gender)))
|
||||
end
|
||||
|
||||
return Ops
|
||||
|
||||
@@ -26,6 +26,7 @@ function State.new()
|
||||
-- App.load's opts; nil in a bare `love . --editor` run.
|
||||
version = nil,
|
||||
slotId = nil,
|
||||
modRoots = nil,
|
||||
-- Hosted inside the launcher process (Edit on a save row) rather than a
|
||||
-- standalone `--editor` window: Close returns to the launcher instead of
|
||||
-- quitting, and App calls onClose() to do it.
|
||||
|
||||
@@ -101,6 +101,31 @@ local function drawMoney(S, Kit, x, y, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
-- engine/menus/init_gender.asm:55-56
|
||||
local GENDERS = { { "BOY", "male" }, { "GIRL", "female" } }
|
||||
|
||||
local function trainerHeight(Kit, s, pad)
|
||||
return pad * 2 + Kit.textHeight("caption") + 10 * s + 28 * s
|
||||
end
|
||||
|
||||
local function drawTrainer(S, Kit, x, y, w, h)
|
||||
local s = Kit.scale
|
||||
local pad = 16 * s
|
||||
Kit.card(x, y, w, h)
|
||||
Kit.caption(x + pad, y + pad, "TRAINER")
|
||||
Kit.textRight("mono", tostring((S.save.player and S.save.player.name) or "?"),
|
||||
x + w - pad, y + pad, PAL.caption)
|
||||
local cy = y + pad + Kit.textHeight("caption") + 10 * s
|
||||
local chipW = (w - 2 * pad - 8 * s) / 2
|
||||
local gender = Gen.playerGender(S.save)
|
||||
for i, pair in ipairs(GENDERS) do
|
||||
if Kit.chip(x + pad + (i - 1) * (chipW + 8 * s), cy, chipW, 28 * s,
|
||||
pair[1], gender == pair[2], PAL.green, PAL.steel) then
|
||||
Ops.setPlayerGender(S, pair[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local BADGE_COLS = 4
|
||||
|
||||
local function badgeHeight(S, Kit, s, pad)
|
||||
@@ -273,6 +298,10 @@ function M.draw(S, Kit, x, y, w, h)
|
||||
local listH = 300 * s
|
||||
Kit.pushClip(x, y, w, h)
|
||||
local cy = y - off
|
||||
if Gen.hasPlayerGender(S.save, S.version) then
|
||||
local trainerH = trainerHeight(Kit, s, pad)
|
||||
drawTrainer(S, Kit, x, cy, w, trainerH); cy = cy + trainerH + gap
|
||||
end
|
||||
drawMoney(S, Kit, x, cy, w, moneyH); cy = cy + moneyH + gap
|
||||
drawPicker(S, Kit, x, cy, w, pickH); cy = cy + pickH + gap
|
||||
drawBadges(S, Kit, x, cy, w, badgeH); cy = cy + badgeH + gap
|
||||
@@ -294,8 +323,14 @@ function M.draw(S, Kit, x, y, w, h)
|
||||
-- made the old panel unusable.
|
||||
local moneyH = moneyHeight(Kit, s, pad, S)
|
||||
local badgeH = badgeHeight(S, Kit, s, pad)
|
||||
drawMoney(S, Kit, x, y, leftW, moneyH)
|
||||
drawPicker(S, Kit, x, y + moneyH + gap, leftW, h - moneyH - badgeH - 2 * gap)
|
||||
local topH = 0
|
||||
if Gen.hasPlayerGender(S.save, S.version) then
|
||||
topH = trainerHeight(Kit, s, pad) + gap
|
||||
drawTrainer(S, Kit, x, y, leftW, topH - gap)
|
||||
end
|
||||
drawMoney(S, Kit, x, y + topH, leftW, moneyH)
|
||||
drawPicker(S, Kit, x, y + topH + moneyH + gap, leftW,
|
||||
h - topH - moneyH - badgeH - 2 * gap)
|
||||
drawBadges(S, Kit, x, y + h - badgeH, leftW, badgeH)
|
||||
drawBag(S, Kit, bagX, y, listW, h)
|
||||
drawPc(S, Kit, pcX, y, listW, h)
|
||||
|
||||
@@ -147,6 +147,66 @@ local function drawDvRows(S, Kit, mon, cx, rowY, colW, rowH, rowGap)
|
||||
end
|
||||
end
|
||||
|
||||
-- engine/pokemon/caught_data.asm:168-199
|
||||
local CAUGHT_BY = { { "-", "none" }, { "BOY", "boy" }, { "GIRL", "girl" } }
|
||||
|
||||
local function drawCaughtRows(S, Kit, mon, cx, y, inner, row)
|
||||
local s = Kit.scale
|
||||
local chipH = 22 * s
|
||||
local gap = 6 * s
|
||||
local tinyH = Kit.textHeight("tiny")
|
||||
|
||||
Kit.text("tiny", "CAUGHT", cx, y + (row - tinyH) / 2, PAL.caption)
|
||||
local timeX = cx + 62 * s
|
||||
local timeW = math.max(34 * s, (cx + inner - timeX - 3 * gap) / 4)
|
||||
for i, label in ipairs(Ops.CAUGHT_TIMES) do
|
||||
if Kit.chip(timeX + (i - 1) * (timeW + gap), y + (row - chipH) / 2,
|
||||
timeW, chipH, label, (mon.caughtTime or 0) == i - 1, PAL.blue, PAL.steel) then
|
||||
Ops.setCaughtTime(S, mon, i - 1)
|
||||
end
|
||||
end
|
||||
y = y + row + gap
|
||||
|
||||
local btn = 24 * s
|
||||
local lvX = cx + inner - 2 * btn - gap
|
||||
if Kit.stepper(lvX, y + (row - btn) / 2, btn, btn, "-", { font = "small" }) then
|
||||
Ops.setCaughtLevel(S, mon, (mon.caughtLevel or 0) - 1)
|
||||
end
|
||||
if Kit.stepper(lvX + btn + gap, y + (row - btn) / 2, btn, btn, "+",
|
||||
{ font = "small" }) then
|
||||
Ops.setCaughtLevel(S, mon, (mon.caughtLevel or 0) + 1)
|
||||
end
|
||||
Kit.textRight("tiny", ("MET LV %d"):format(mon.caughtLevel or 0), lvX - 10 * s,
|
||||
y + (row - tinyH) / 2, PAL.text)
|
||||
Kit.text("tiny", "OT", cx, y + (row - tinyH) / 2, PAL.caption)
|
||||
local otX = cx + 26 * s
|
||||
local otW = math.max(30 * s, (inner * 0.42 - 26 * s - 2 * gap) / 3)
|
||||
for i, pair in ipairs(CAUGHT_BY) do
|
||||
if Kit.chip(otX + (i - 1) * (otW + gap), y + (row - chipH) / 2, otW, chipH,
|
||||
pair[1], (mon.caughtByGender or "none") == pair[2], PAL.blue, PAL.steel) then
|
||||
Ops.setCaughtByGender(S, mon, pair[2])
|
||||
end
|
||||
end
|
||||
y = y + row + gap
|
||||
|
||||
local whereX = cx + inner - 3 * btn - 2 * gap
|
||||
if Kit.stepper(whereX, y + (row - btn) / 2, btn, btn, "-", { font = "small" }) then
|
||||
Ops.setCaughtLocation(S, mon, (mon.caughtLocation or 0) - 1)
|
||||
end
|
||||
if Kit.stepper(whereX + btn + gap, y + (row - btn) / 2, btn, btn, "+",
|
||||
{ font = "small" }) then
|
||||
Ops.setCaughtLocation(S, mon, (mon.caughtLocation or 0) + 1)
|
||||
end
|
||||
if Kit.button(whereX + 2 * (btn + gap), y + (row - btn) / 2, btn, btn, "0",
|
||||
{ kind = "danger", font = "micro", radius = 6 * s }) then
|
||||
Ops.setCaughtLocation(S, mon, 0)
|
||||
end
|
||||
local where = ("WHERE %s"):format(Gen.landmarkName(S.data, mon.caughtLocation or 0))
|
||||
Kit.text("tiny", Kit.ellipsize("tiny", where, whereX - 10 * s - cx), cx,
|
||||
y + (row - tinyH) / 2, PAL.text)
|
||||
return y + row + gap
|
||||
end
|
||||
|
||||
local function drawMoveRows(S, Kit, mon, rightX, rowY, colW, rowH, rowGap)
|
||||
local s = Kit.scale
|
||||
for slot = 1, 4 do
|
||||
@@ -231,6 +291,7 @@ function MonEditor.draw(S, Kit, x, y, w, h)
|
||||
-- the field + Set row
|
||||
local extraH = 0
|
||||
if Gen.ofState(S) == 2 then extraH = 88 * s end
|
||||
if Gen.hasCaughtData(S.save, S.version) then extraH = extraH + 102 * s end
|
||||
local nickFieldH = 30 * s
|
||||
local contentH = pad + headerH + 18 * s
|
||||
+ capH + 10 * s + nickFieldH + 18 * s
|
||||
@@ -341,7 +402,7 @@ function MonEditor.draw(S, Kit, x, y, w, h)
|
||||
local colY = statsY + cellH + 18 * s
|
||||
if Gen.ofState(S) == 2 then
|
||||
local extraY = colY
|
||||
Kit.caption(cx, extraY, "GOLD")
|
||||
Kit.caption(cx, extraY, Gen.editionLabel(S.save, S.version))
|
||||
extraY = extraY + capH + 8 * s
|
||||
local row = 28 * s
|
||||
Kit.text("tiny", "HELD " .. tostring(mon.item or "none"), cx, extraY, PAL.text)
|
||||
@@ -371,7 +432,11 @@ function MonEditor.draw(S, Kit, x, y, w, h)
|
||||
if mon.unownLetter then bits[#bits + 1] = "Unown " .. tostring(mon.unownLetter) end
|
||||
Kit.text("tiny", table.concat(bits, " ") ~= "" and table.concat(bits, " ")
|
||||
or "gender/shiny follow DVs", cx, extraY, PAL.caption)
|
||||
colY = extraY + 22 * s
|
||||
extraY = extraY + 22 * s
|
||||
if Gen.hasCaughtData(S.save, S.version) then
|
||||
extraY = drawCaughtRows(S, Kit, mon, cx, extraY, inner, row)
|
||||
end
|
||||
colY = extraY
|
||||
end
|
||||
if narrow then
|
||||
-- stacked: DVs first, then moves, then the two actions side by side at
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
target/
|
||||
Generated
+955
@@ -0,0 +1,955 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2f926cc3060f09db9ebc5b52823d85268d24bb917e472c0c4bea35780a7d"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
||||
|
||||
[[package]]
|
||||
name = "glslang"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0db73ac0e8bd6f9c253b70205f2bb1c63845b6e263665ac8063316ce16f408c8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"glslang-sys",
|
||||
"rustc-hash 2.1.3",
|
||||
"smartstring",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glslang-sys"
|
||||
version = "0.8.1+275822a"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec80baca8831e8414db40c562fd76ca6a32a792c1db990451d4e7d3cb246e1ed"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"glob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "halfbrown"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09"
|
||||
dependencies = [
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"foldhash 0.2.0",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png",
|
||||
"zune-core",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.17.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "librashader-common"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75fcb7b016f592e08241eb627ab94d8b2e435769b99f2705a2ba11c063a1b5fc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"halfbrown",
|
||||
"num-traits",
|
||||
"rustc-hash 2.1.3",
|
||||
"serde",
|
||||
"strumbra",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librashader-pack"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e1405d8ae8996621cacbc5d1f9220486eda4cafaf229da02c02eecfbbe2cf"
|
||||
dependencies = [
|
||||
"image",
|
||||
"librashader-common",
|
||||
"librashader-preprocess",
|
||||
"librashader-presets",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librashader-preprocess"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62507ee532a6f3320acac509e3d3b32e8828c8bd1161a74bc576351729c820e2"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"librashader-common",
|
||||
"nom",
|
||||
"serde",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librashader-presets"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d70306212493fbb4be617cdb98b7048cb896b16182951aeb17856bee3d14aeff"
|
||||
dependencies = [
|
||||
"librashader-common",
|
||||
"librashader-preprocess",
|
||||
"nom",
|
||||
"nom_locate",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"thiserror 2.0.20",
|
||||
"vec_extract_if_polyfill",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librashader-reflect"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923a7ea3724e2fca281a08598217fe2daa5b6a245a278448251c851f19c0b290"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytemuck",
|
||||
"glslang",
|
||||
"librashader-common",
|
||||
"librashader-pack",
|
||||
"librashader-preprocess",
|
||||
"librashader-presets",
|
||||
"naga",
|
||||
"rspirv",
|
||||
"rustc-hash 2.1.3",
|
||||
"spirv",
|
||||
"spirv-cross2",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moxcms"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "30.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23bf0a141a9ab6f07dbb492db53245e464bc9db42f407772d9ae03d83a2c1033"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"codespan-reporting",
|
||||
"half",
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"libm",
|
||||
"log",
|
||||
"naga-types",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
"rustc-hash 1.1.0",
|
||||
"serde",
|
||||
"spirv",
|
||||
"thiserror 2.0.20",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga-types"
|
||||
version = "30.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "658200ddc25c6c7b860747516d132d1b284c0fafb7a380233acee9a72fb30e11"
|
||||
dependencies = [
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"rustc-hash 1.1.0",
|
||||
"serde",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom_locate"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"memchr",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rspirv"
|
||||
version = "0.13.0+sdk-1.4.341.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "091dca2e1d6fd3098417b5ec88e77e80d1ba5945750943419dc976858082c296"
|
||||
dependencies = [
|
||||
"rustc-hash 1.1.0",
|
||||
"spirv",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
||||
|
||||
[[package]]
|
||||
name = "smartstring"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"static_assertions",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spike"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"librashader-common",
|
||||
"librashader-preprocess",
|
||||
"librashader-presets",
|
||||
"librashader-reflect",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv"
|
||||
version = "0.4.0+sdk-1.4.341.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv-cross-sys"
|
||||
version = "0.7.2+38681a3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9291b270f65ee1e61842d0a4dadc707d3b1669d8001e0900433e341c897d5d1b"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cc",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv-cross2"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e01f93f6f1637a1a72b43d2ca50ebbcd94c3f4c4e94eda8e27144834671f008"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytemuck",
|
||||
"memchr",
|
||||
"spirv",
|
||||
"spirv-cross-sys",
|
||||
"spirv-cross2-derive",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv-cross2-derive"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18017a288e6ce64dd5d56510166baeabb01849483555c031f573c091b6934a64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strumbra"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b498044485e2789b38e047fdb4c7ac827bd72d66407af9e5d15626f9c45364e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "vec_extract_if_polyfill"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c9cb5fb67c2692310b6eb3fce7dd4b6e4c9a75be4f2f46b27f0b2b7799759c"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "spike"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
name = "librashader_bridge"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
librashader-preprocess = "0.12.0"
|
||||
librashader-presets = "0.12.0"
|
||||
librashader-reflect = "0.12.0"
|
||||
librashader-common = "0.12.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use librashader_presets::{ShaderFeatures, ShaderPreset};
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use librashader_reflect::back::glsl::GlslVersion;
|
||||
use librashader_bridge::compile_pass_glsl;
|
||||
|
||||
fn count_uniform_vectors(glsl: &str) -> (usize, Vec<String>) {
|
||||
let mut total = 0usize;
|
||||
let mut lines = Vec::new();
|
||||
for line in glsl.lines() {
|
||||
let t = line.trim();
|
||||
if !t.starts_with("uniform ") {
|
||||
continue;
|
||||
}
|
||||
// crude: skip sampler uniforms, they don't cost a fragment uniform *vector* slot
|
||||
if t.contains("sampler") {
|
||||
lines.push(format!("(sampler, not counted) {t}"));
|
||||
continue;
|
||||
}
|
||||
let cost = if t.contains("mat4") {
|
||||
4
|
||||
} else if t.contains("mat3") {
|
||||
3
|
||||
} else if t.contains("mat2") {
|
||||
2
|
||||
} else {
|
||||
1 // scalar, vec2, vec3, vec4 all cost one vec4 slot each
|
||||
};
|
||||
// arrays: uniform float foo[8]; costs 8 slots
|
||||
let mut n = cost;
|
||||
if let Some(br) = t.find('[') {
|
||||
if let Some(close) = t[br..].find(']') {
|
||||
if let Ok(count) = t[br + 1..br + close].trim().parse::<usize>() {
|
||||
n = cost * count;
|
||||
}
|
||||
}
|
||||
}
|
||||
total += n;
|
||||
lines.push(format!("({n} slot{}) {t}", if n == 1 { "" } else { "s" }));
|
||||
}
|
||||
(total, lines)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let preset_path = PathBuf::from(
|
||||
"../references/slang-shaders-gameboy/handheld/gameboy-color-dot-matrix.slangp",
|
||||
);
|
||||
println!("=== parsing preset: {} ===", preset_path.display());
|
||||
let preset = ShaderPreset::try_parse(&preset_path, ShaderFeatures::empty())?;
|
||||
println!("pass_count = {}", preset.pass_count);
|
||||
for pass in &preset.passes {
|
||||
println!(
|
||||
" pass {} alias={:?} -> {}",
|
||||
pass.meta.id,
|
||||
pass.meta.alias,
|
||||
pass.path.display()
|
||||
);
|
||||
}
|
||||
for tex in &preset.textures {
|
||||
println!(
|
||||
" texture {} -> {} (linear={:?})",
|
||||
tex.meta.name,
|
||||
tex.path.display(),
|
||||
tex.meta.filter_mode
|
||||
);
|
||||
}
|
||||
println!();
|
||||
|
||||
std::fs::create_dir_all("out")?;
|
||||
|
||||
for pass in &preset.passes {
|
||||
let label = format!(
|
||||
"pass{}{}",
|
||||
pass.meta.id,
|
||||
pass.meta
|
||||
.alias
|
||||
.as_ref()
|
||||
.map(|a| format!("_{a}"))
|
||||
.unwrap_or_default()
|
||||
);
|
||||
println!("=== {label}: {} ===", pass.path.display());
|
||||
|
||||
let source = match ShaderSource::load(&pass.path, preset.features) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
println!(" !! ShaderSource::load failed: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
println!(" parameters: {}", source.parameters.len());
|
||||
for (name, p) in &source.parameters {
|
||||
println!(
|
||||
" {name} = {} (min {} max {} step {})",
|
||||
p.initial, p.minimum, p.maximum, p.step
|
||||
);
|
||||
}
|
||||
|
||||
for (tag, version) in [("100es", GlslVersion::Glsl100Es), ("120", GlslVersion::Glsl120)] {
|
||||
match compile_pass_glsl(&source, version) {
|
||||
Ok((vert, frag)) => {
|
||||
let (slots, lines) = count_uniform_vectors(&frag);
|
||||
println!(" -- GLSL {tag}: fragment uniform-vector cost = {slots}");
|
||||
for l in &lines {
|
||||
println!(" {l}");
|
||||
}
|
||||
let out_path = format!("out/{label}_{tag}.frag.glsl");
|
||||
std::fs::write(&out_path, &frag)?;
|
||||
println!(" written to {out_path}");
|
||||
let vert_path = format!("out/{label}_{tag}.vert.glsl");
|
||||
std::fs::write(&vert_path, &vert)?;
|
||||
println!(" written to {vert_path}");
|
||||
}
|
||||
Err(e) => {
|
||||
println!(" !! GLSL {tag} compile failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user