Compare commits
No commits in common. "master" and "2.2.0" have entirely different histories.
78
.github/workflows/python.yml
vendored
|
|
@ -1,8 +1,6 @@
|
|||
name: Python Package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
|
|
@ -10,74 +8,6 @@ on:
|
|||
- '*'
|
||||
|
||||
jobs:
|
||||
mypy-required:
|
||||
runs-on: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Workaround for worktree config
|
||||
run: git config --unset-all extensions.worktreeConfig || true
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
cache: "pip"
|
||||
|
||||
- name: Install Python packages
|
||||
run: |
|
||||
python -m pip install -e .
|
||||
python -m pip install mypy
|
||||
|
||||
- name: Mypy on required files
|
||||
uses: tsuyoshicho/action-mypy@v5
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
# Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review].
|
||||
reporter: github-check
|
||||
setup_method: nothing
|
||||
target: --config-file=pyproject.toml
|
||||
fail_on_error: true
|
||||
install_types: false
|
||||
|
||||
mypy-modified:
|
||||
runs-on: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Workaround for worktree config
|
||||
run: git config --unset-all extensions.worktreeConfig || true
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
cache: "pip"
|
||||
|
||||
- name: Install Python packages
|
||||
run: |
|
||||
python -m pip install -e .
|
||||
python -m pip install mypy
|
||||
|
||||
- name: Mypy on modified files
|
||||
uses: tsuyoshicho/action-mypy@v5
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
# Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review].
|
||||
reporter: github-check
|
||||
setup_method: nothing
|
||||
level: warning
|
||||
fail_on_error: false
|
||||
install_types: false
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Wheel
|
||||
|
|
@ -92,7 +22,7 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Install Python packages
|
||||
run: python -m pip install --upgrade build pip
|
||||
|
|
@ -104,7 +34,7 @@ jobs:
|
|||
run: python -m build --sdist
|
||||
|
||||
- name: Store the packages
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist
|
||||
|
|
@ -112,13 +42,11 @@ jobs:
|
|||
pypi:
|
||||
runs-on: 'ubuntu-latest'
|
||||
needs:
|
||||
- mypy-required
|
||||
- build
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
|
|
|||
33
.github/workflows/workflow-validate.yml
vendored
|
|
@ -1,33 +0,0 @@
|
|||
name: Check Workflow Files
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
merge_group:
|
||||
pull_request:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install action-validator with asdf
|
||||
uses: asdf-vm/actions/install@v3
|
||||
with:
|
||||
tool_versions: |
|
||||
action-validator 0.5.1
|
||||
|
||||
- name: Lint Actions
|
||||
run: |
|
||||
find .github/workflows -type f \( -iname \*.yaml -o -iname \*.yml \) \
|
||||
| xargs -I {} action-validator --verbose {}
|
||||
17
.gitignore
vendored
|
|
@ -1,6 +1,12 @@
|
|||
data/*
|
||||
notes/*
|
||||
|
||||
pre_edited_cs/Doukutsu\.exe\.blbkp
|
||||
|
||||
pre_edited_cs/Profile*
|
||||
|
||||
pre_edited_cs/window\.rect
|
||||
|
||||
*Copy/
|
||||
|
||||
venv/
|
||||
|
|
@ -11,12 +17,5 @@ dist/
|
|||
|
||||
cave_story_randomizer.egg-info/
|
||||
|
||||
**/__pycache__
|
||||
|
||||
pre_edited_cs/**/Doukutsu\.exe\.blbkp
|
||||
pre_edited_cs/**/Profile*
|
||||
pre_edited_cs/**/window\.rect
|
||||
|
||||
pre_edited_cs/data/version.txt
|
||||
|
||||
pre_edited_cs/freeware/Doukutsu_backup.exe
|
||||
*/__pycache__
|
||||
caver/version.py
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.8.3
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [ --fix, --exit-non-zero-on-fix ]
|
||||
- id: ruff-format
|
||||
|
||||
- repo: https://github.com/henriquegemignani/jsonschema-to-typeddict
|
||||
rev: v1.1.1
|
||||
hooks:
|
||||
- id: jsonschema-to-typeddict
|
||||
files: caver/schema/schema.json
|
||||
args: [ --output-path, caver/schema/types.py, --root-name, CaverData ]
|
||||
142
caver/patcher.py
|
|
@ -1,44 +1,20 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import platform as pl
|
||||
import shutil
|
||||
import sys
|
||||
import textwrap
|
||||
import typing
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Callable, Optional
|
||||
from uuid import UUID
|
||||
from lupa import LuaRuntime
|
||||
import logging
|
||||
import shutil
|
||||
import textwrap
|
||||
import sys
|
||||
|
||||
import pre_edited_cs
|
||||
from randovania_lupa import LuaRuntime # type: ignore
|
||||
|
||||
from caver.schema.validator_with_default import DefaultValidatingDraft7Validator
|
||||
|
||||
LuaFile = typing.Any
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from caver.schema import (
|
||||
CaverData,
|
||||
CaverdataMaps,
|
||||
CaverdataOtherTsc,
|
||||
EventNumber,
|
||||
MapName,
|
||||
)
|
||||
|
||||
CSVERSION = 5
|
||||
|
||||
class CaverException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CSPlatform(Enum):
|
||||
FREEWARE = "freeware"
|
||||
TWEAKED = "tweaked"
|
||||
|
||||
|
||||
def get_path() -> Path:
|
||||
if getattr(sys, "frozen", False):
|
||||
file_dir = Path(getattr(sys, "_MEIPASS"))
|
||||
|
|
@ -46,91 +22,56 @@ def get_path() -> Path:
|
|||
file_dir = Path(__file__).parent.parent
|
||||
return file_dir.joinpath("caver")
|
||||
|
||||
|
||||
def validate(patch_data: dict) -> None:
|
||||
with Path(__file__).parent.joinpath("schema/schema.json").open() as f:
|
||||
schema = json.load(f)
|
||||
DefaultValidatingDraft7Validator(schema).validate(patch_data)
|
||||
|
||||
|
||||
def patch_files(
|
||||
patch_data: CaverData, output_dir: Path, platform: CSPlatform, progress_update: Callable[[str, float], None]
|
||||
) -> None:
|
||||
progress_update("Validating schema...", -1)
|
||||
validate(typing.cast(dict, patch_data))
|
||||
|
||||
def patch_files(patch_data: dict, output_dir: Path, progress_update: Callable[[str, float], None]):
|
||||
progress_update("Copying base files...", -1)
|
||||
ensure_base_files_exist(platform, output_dir)
|
||||
ensure_base_files_exist(output_dir)
|
||||
|
||||
total = len(patch_data["maps"].keys()) + len(patch_data["other_tsc"].keys()) + 3
|
||||
|
||||
lua_file = get_path().joinpath("tsc_file.lua").read_text()
|
||||
TscFile = typing.cast(LuaFile, LuaRuntime().execute(lua_file))
|
||||
TscFile = LuaRuntime().execute(lua_file)
|
||||
|
||||
for i, (mapname, mapdata) in enumerate(patch_data["maps"].items()):
|
||||
progress_update(f"Patching {mapname}...", i / total)
|
||||
progress_update(f"Patching {mapname}...", i/total)
|
||||
patch_map(mapname, mapdata, TscFile, output_dir)
|
||||
|
||||
for filename, scripts in patch_data["other_tsc"].items():
|
||||
i += 1
|
||||
progress_update(f"Patching {filename}.tsc...", i / total)
|
||||
progress_update(f"Patching {filename}.tsc...", i/total)
|
||||
patch_other(filename, scripts, TscFile, output_dir)
|
||||
|
||||
i += 1
|
||||
progress_update("Copying MyChar...", i / total)
|
||||
patch_mychar(patch_data["mychar"], output_dir, platform is CSPlatform.TWEAKED)
|
||||
progress_update("Copying MyChar...", i/total)
|
||||
patch_mychar(patch_data["mychar"], output_dir)
|
||||
|
||||
i += 1
|
||||
progress_update("Copying hash...", i / total)
|
||||
progress_update("Copying hash...", i/total)
|
||||
patch_hash(patch_data["hash"], output_dir)
|
||||
|
||||
i += 1
|
||||
progress_update("Copying UUID...", i / total)
|
||||
progress_update("Copying UUID...", i/total)
|
||||
patch_uuid(patch_data["uuid"], output_dir)
|
||||
|
||||
if platform == CSPlatform.TWEAKED:
|
||||
if pl.system() == "Linux":
|
||||
output_dir.joinpath("CSTweaked.exe").unlink()
|
||||
else:
|
||||
output_dir.joinpath("CSTweaked").unlink()
|
||||
|
||||
|
||||
def ensure_base_files_exist(platform: CSPlatform, output_dir: Path) -> None:
|
||||
def ensure_base_files_exist(output_dir: Path):
|
||||
internal_copy = pre_edited_cs.get_path()
|
||||
|
||||
with internal_copy.joinpath("data", "version.txt").open() as version_file:
|
||||
latest_version = version_file.readline()
|
||||
version = output_dir.joinpath("data", "Stage", "_version.txt")
|
||||
keep_existing_files = version.exists() and int(version.read_text()) >= CSVERSION
|
||||
|
||||
version = output_dir.joinpath("data", "version.txt")
|
||||
current_version = "v0.0.0.0"
|
||||
if version.exists():
|
||||
with version.open() as version_file:
|
||||
current_version = version_file.readline()
|
||||
|
||||
keep_existing_files = current_version >= latest_version
|
||||
|
||||
def should_ignore(path: str, names: list[str]) -> list[str]:
|
||||
def should_ignore(path: str, names: list[str]):
|
||||
base = ["__init__.py", "__pycache__", "ScriptSource", "__pyinstaller"]
|
||||
if keep_existing_files:
|
||||
p = Path(path)
|
||||
base.extend(
|
||||
[str(p.joinpath(name)) for name in names if p.joinpath(name).exists() and p.joinpath(name).is_file()]
|
||||
)
|
||||
base.extend([p.joinpath(name) for name in names if p.joinpath(name).exists() and p.joinpath(name).is_file()])
|
||||
return base
|
||||
|
||||
try:
|
||||
shutil.copytree(internal_copy.joinpath(platform.value), output_dir, ignore=should_ignore, dirs_exist_ok=True)
|
||||
shutil.copytree(
|
||||
internal_copy.joinpath("data"), output_dir.joinpath("data"), ignore=should_ignore, dirs_exist_ok=True
|
||||
)
|
||||
shutil.copytree(internal_copy, output_dir, ignore=should_ignore, dirs_exist_ok=True)
|
||||
except shutil.Error:
|
||||
raise CaverException(
|
||||
"Error copying base files. Ensure the directory is not read-only, and that Doukutsu.exe is closed"
|
||||
)
|
||||
raise CaverException("Error copying base files. Ensure the directory is not read-only, and that Doukutsu.exe is closed")
|
||||
output_dir.joinpath("data", "Plaintext").mkdir(exist_ok=True)
|
||||
|
||||
|
||||
def patch_map(mapname: MapName, mapdata: CaverdataMaps, TscFile: LuaFile, output_dir: Path) -> None:
|
||||
def patch_map(mapname: str, mapdata: dict[str, dict], TscFile, output_dir: Path):
|
||||
mappath = output_dir.joinpath("data", "Stage", f"{mapname}.tsc")
|
||||
tsc_file = TscFile.new(TscFile, mappath.read_bytes(), logging.getLogger("caver"))
|
||||
|
||||
|
|
@ -141,7 +82,7 @@ def patch_map(mapname: MapName, mapdata: CaverdataMaps, TscFile: LuaFile, output
|
|||
TscFile.placeSongAtCue(tsc_file, song["song_id"], event, song["original_id"], mapname)
|
||||
|
||||
for event, script in mapdata["entrances"].items():
|
||||
needle = "<EVE...." # TODO: create a proper pattern
|
||||
needle = "<EVE...." # TODO: create a proper pattern
|
||||
TscFile.placeScriptAtEvent(tsc_file, script, event, mapname, needle)
|
||||
|
||||
for event, hint in mapdata["hints"].items():
|
||||
|
|
@ -152,10 +93,7 @@ def patch_map(mapname: MapName, mapdata: CaverdataMaps, TscFile: LuaFile, output
|
|||
mappath.write_bytes(bytes(chars))
|
||||
output_dir.joinpath("data", "Plaintext", f"{mapname}.txt").write_text(TscFile.getPlaintext(tsc_file))
|
||||
|
||||
|
||||
def patch_other(
|
||||
filename: MapName, scripts: dict[EventNumber, CaverdataOtherTsc], TscFile: LuaFile, output_dir: Path
|
||||
) -> None:
|
||||
def patch_other(filename: str, scripts: dict[str, dict[str, str]], TscFile, output_dir: Path):
|
||||
filepath = output_dir.joinpath("data", f"{filename}.tsc")
|
||||
tsc_file = TscFile.new(TscFile, filepath.read_bytes(), logging.getLogger("caver"))
|
||||
|
||||
|
|
@ -166,30 +104,20 @@ def patch_other(
|
|||
filepath.write_bytes(bytes(chars))
|
||||
output_dir.joinpath("data", "Plaintext", f"{filename}.txt").write_text(TscFile.getPlaintext(tsc_file))
|
||||
|
||||
|
||||
def patch_mychar(mychar: str | None, output_dir: Path, add_upscale: bool) -> None:
|
||||
def patch_mychar(mychar: Optional[str], output_dir: Path):
|
||||
if mychar is None:
|
||||
return
|
||||
mychar_img = Path(mychar).read_bytes()
|
||||
output_dir.joinpath("data", "MyChar.bmp").write_bytes(mychar_img)
|
||||
|
||||
if add_upscale:
|
||||
mychar_name = Path(mychar).name
|
||||
mychar_up_img = Path(mychar).parent.joinpath("2x", mychar_name).read_bytes()
|
||||
output_dir.joinpath("data", "sprites_up", "MyChar.bmp").write_bytes(mychar_up_img)
|
||||
|
||||
|
||||
def patch_hash(hash: list[int], output_dir: Path) -> None:
|
||||
def patch_hash(hash: list[int], output_dir: Path):
|
||||
hash_strings = [f"{num:04d}" for num in hash]
|
||||
hash_string = ",".join(hash_strings)
|
||||
output_dir.joinpath("data", "hash.txt").write_text(hash_string)
|
||||
|
||||
|
||||
def patch_uuid(uuid: str, output_dir: Path) -> None:
|
||||
uuid = f"{{{UUID(uuid)}}}"
|
||||
def patch_uuid(uuid: str, output_dir: Path):
|
||||
output_dir.joinpath("data", "uuid.txt").write_text(uuid)
|
||||
|
||||
|
||||
def wrap_msg_text(text: str, facepic: bool, *, ending: str = "<NOD", max_text_boxes: int | None = 1) -> str:
|
||||
hard_limit = 35
|
||||
msgbox_limit = 26 if facepic else hard_limit
|
||||
|
|
@ -198,21 +126,19 @@ def wrap_msg_text(text: str, facepic: bool, *, ending: str = "<NOD", max_text_bo
|
|||
lines = textwrap.wrap(text, width=msgbox_limit, max_lines=max_lines)
|
||||
|
||||
text = ""
|
||||
for i, line in enumerate(lines):
|
||||
text += line
|
||||
if i < len(lines) - 1:
|
||||
for i, l in enumerate(lines):
|
||||
text += l
|
||||
if i < len(lines)-1:
|
||||
if i % 3 == 2:
|
||||
text += "<NOD"
|
||||
if len(line) != hard_limit:
|
||||
if len(l) != hard_limit:
|
||||
text += "\r\n"
|
||||
text += ending
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def create_hint_script(text: str, facepic: bool, ending: str) -> str:
|
||||
"""
|
||||
A desperate attempt to generate valid <MSG text.
|
||||
Fills one text box (up to three lines). Attempts to wrap words elegantly.
|
||||
A desperate attempt to generate valid <MSG text. Fills one text box (up to three lines). Attempts to wrap words elegantly.
|
||||
"""
|
||||
return f"<PRI<MSG<TUR{wrap_msg_text(text, facepic, ending=ending)}"
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
from caver.schema.types import (
|
||||
CaverData,
|
||||
CaverdataMaps,
|
||||
CaverdataMapsHints,
|
||||
CaverdataMapsMusic,
|
||||
CaverdataOtherTsc,
|
||||
EventNumber,
|
||||
MapName,
|
||||
TscScript,
|
||||
TscValue,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CaverData",
|
||||
"TscValue",
|
||||
"EventNumber",
|
||||
"MapName",
|
||||
"TscScript",
|
||||
"CaverdataMapsHints",
|
||||
"CaverdataMapsMusic",
|
||||
"CaverdataMaps",
|
||||
"CaverdataOtherTsc",
|
||||
]
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"maps": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/map_name"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pickups": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/event_number"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/tsc_script"
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"hints": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/event_number"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"facepic": {
|
||||
"$ref": "#/$defs/tsc_value",
|
||||
"default": "0000"
|
||||
},
|
||||
"ending": {
|
||||
"$ref": "#/$defs/tsc_script",
|
||||
"default": "<END"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"text",
|
||||
"facepic",
|
||||
"ending"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"music": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/event_number"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"original_id": {
|
||||
"$ref": "#/$defs/tsc_value"
|
||||
},
|
||||
"song_id": {
|
||||
"$ref": "#/$defs/tsc_value"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"original_id",
|
||||
"song_id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"entrances": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/event_number"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/tsc_script"
|
||||
},
|
||||
"default": {}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"pickups",
|
||||
"hints",
|
||||
"music",
|
||||
"entrances"
|
||||
]
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"other_tsc": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/map_name"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "#/$defs/event_number"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"needle": {
|
||||
"type": "string",
|
||||
"description": "Lua pattern to search for and replace within the event"
|
||||
},
|
||||
"script": {
|
||||
"$ref": "#/$defs/tsc_script"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"needle",
|
||||
"script"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"mychar": {
|
||||
"description": "A path to the mychar.bmp file to use (Optional)",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^.*\\.bmp$"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"hash": {
|
||||
"description": "An array of five item IDs to display on the title screen, within [1, 39]",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 39
|
||||
},
|
||||
"minItems": 5,
|
||||
"maxItems": 5,
|
||||
"default": [1,1,1,1,1]
|
||||
},
|
||||
"uuid": {
|
||||
"description": "The UUID for the world in a multiworld session, or the empty UUID if single player.",
|
||||
"type": "string",
|
||||
"pattern": "^\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}$",
|
||||
"default": "{00000000-0000-0000-0000-000000000000}"
|
||||
},
|
||||
"platform": {
|
||||
"description": "Which supported platform to export to.",
|
||||
"$comment": "Not actually used by the patcher.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"freeware",
|
||||
"tweaked"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"maps",
|
||||
"other_tsc",
|
||||
"mychar",
|
||||
"hash",
|
||||
"uuid"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"tsc_value": {
|
||||
"type": "string",
|
||||
"pattern": "^[ -~]{4}$"
|
||||
},
|
||||
"event_number": {
|
||||
"$ref": "#/$defs/tsc_value",
|
||||
"$comment": "Semantic alias for tsc_value"
|
||||
},
|
||||
"map_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tsc_script": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
# This file is generated. Manual changes will be lost
|
||||
# fmt: off
|
||||
# ruff: noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import typing_extensions as typ
|
||||
|
||||
|
||||
# Definitions
|
||||
TscValue: typ.TypeAlias = str
|
||||
EventNumber: typ.TypeAlias = TscValue
|
||||
MapName: typ.TypeAlias = str
|
||||
TscScript: typ.TypeAlias = str
|
||||
|
||||
|
||||
# Schema entries
|
||||
@typ.final
|
||||
class CaverdataMapsHints(typ.TypedDict):
|
||||
text: str
|
||||
facepic: TscValue
|
||||
ending: TscScript
|
||||
|
||||
|
||||
@typ.final
|
||||
class CaverdataMapsMusic(typ.TypedDict):
|
||||
original_id: TscValue
|
||||
song_id: TscValue
|
||||
|
||||
|
||||
@typ.final
|
||||
class CaverdataMaps(typ.TypedDict):
|
||||
pickups: dict[EventNumber, TscScript]
|
||||
hints: dict[EventNumber, CaverdataMapsHints]
|
||||
music: dict[EventNumber, CaverdataMapsMusic]
|
||||
entrances: dict[EventNumber, TscScript]
|
||||
|
||||
|
||||
@typ.final
|
||||
class CaverdataOtherTsc(typ.TypedDict):
|
||||
needle: str
|
||||
script: TscScript
|
||||
|
||||
|
||||
|
||||
@typ.final
|
||||
class Caverdata(typ.TypedDict):
|
||||
maps: dict[MapName, CaverdataMaps]
|
||||
other_tsc: dict[MapName, dict[EventNumber, CaverdataOtherTsc]]
|
||||
mychar: None | str
|
||||
hash: list[int]
|
||||
uuid: str
|
||||
platform: typ.NotRequired[str]
|
||||
|
||||
CaverData: typ.TypeAlias = Caverdata
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
from jsonschema import Draft7Validator, validators
|
||||
|
||||
|
||||
def extend_with_default(validator_class):
|
||||
validate_properties = validator_class.VALIDATORS["properties"]
|
||||
|
||||
def set_defaults(validator, properties, instance, schema):
|
||||
for property, subschema in properties.items():
|
||||
if "default" in subschema:
|
||||
instance.setdefault(property, subschema["default"])
|
||||
|
||||
yield from validate_properties(
|
||||
validator,
|
||||
properties,
|
||||
instance,
|
||||
schema,
|
||||
)
|
||||
|
||||
return validators.extend(
|
||||
validator_class,
|
||||
{"properties": set_defaults},
|
||||
)
|
||||
|
||||
|
||||
DefaultValidatingDraft7Validator = extend_with_default(Draft7Validator)
|
||||
|
|
@ -112,7 +112,7 @@ function TscFile:_getLabelPositionRange(label)
|
|||
end
|
||||
|
||||
if labelStart == nil then
|
||||
error(("Could not find label: %s"):format(label))
|
||||
error(("%s: Could not find label: %s"):format(self.mapName, label))
|
||||
labelStart = 1
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
this file isn't a real hex patch; it's a copy and paste of peri's discord message on 5 dec 2024:
|
||||
|
||||
---
|
||||
|
||||
There is an undocumented hex edit to increase the size of the Casts.bmp surface (0x411569: F0 00 -> E0 01, which I presume we just used BL's hacks/intro/surface_bitmap_sizes.xml here);
|
||||
|
||||
There is an undocumented hex edit to increase the TSC buffer size (0x421545: 00 50 00 -> 20 A1 07, again this was probably from the BL hack and we never wrote it down);
|
||||
|
||||
Randomly the byte at 0x414B24 was changed from 0x80 to 0x5F, which is the number of bytes to memset when initializing the map flag array. Not sure why this was changed, but the multiworld DLL overwrites this function anyways so it doesn't really make a difference (anymore).
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
; Reads version number from data\version.txt
|
||||
; The file must start with the version string in the format shown at the bottom of this file
|
||||
; Made by periwinkle
|
||||
|
||||
#define
|
||||
gDataPath=49E220
|
||||
gVersionString=48C2BC
|
||||
sprintf=481010
|
||||
fopen=480FFD
|
||||
rb=48C28C
|
||||
fread=480F55
|
||||
fclose=480E1B
|
||||
sscanf=4817E8
|
||||
call VirtualProtect=data ff 15 84 c0 48 00
|
||||
filePath=[ebp-114]
|
||||
bufchk=[ebp-10]
|
||||
tmp=[ebp-c]
|
||||
oldProtect=[ebp-8]
|
||||
fp=[ebp-4]
|
||||
v1=[ebp+8]
|
||||
v2=[ebp+c]
|
||||
v3=[ebp+10]
|
||||
v4=[ebp+14]
|
||||
#enddefine
|
||||
|
||||
offset 410990 ; GetCompileVersion
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
sub esp, 114
|
||||
mov eax, [498B20]
|
||||
mov bufchk, eax
|
||||
|
||||
; Get path to data\version.txt file
|
||||
push gDataPath
|
||||
push :VersionPath ; "%s\version.txt"
|
||||
lea edx, filePath
|
||||
push edx
|
||||
call sprintf
|
||||
add esp, c
|
||||
|
||||
; Open file
|
||||
push rb ; "rb"
|
||||
lea ecx, filePath
|
||||
push ecx
|
||||
call fopen
|
||||
add esp, 8
|
||||
test eax, eax
|
||||
jz :ReadVersion
|
||||
mov fp, eax
|
||||
|
||||
; Mark gVersionString as read/write (it's normally in a read-only segment)
|
||||
lea eax, oldProtect
|
||||
push eax
|
||||
push 4 ; PAGE_READWRITE
|
||||
push 40 ; Max size of gVersionString (including null character)
|
||||
push gVersionString
|
||||
call VirtualProtect
|
||||
test eax, eax
|
||||
jz :CloseFile
|
||||
|
||||
; Read contents of file into gVersionString
|
||||
push fp
|
||||
|
||||
push 3F ; count (excluding null character)
|
||||
push 1 ; size
|
||||
push gVersionString
|
||||
call fread
|
||||
add esp, 10
|
||||
mov byte [eax+gVersionString], 0 ; Write null terminator
|
||||
|
||||
; Restore previous protection status
|
||||
lea eax, tmp
|
||||
push eax
|
||||
push oldProtect
|
||||
push 40
|
||||
push gVersionString
|
||||
call VirtualProtect
|
||||
|
||||
:CloseFile
|
||||
push fp
|
||||
call fclose
|
||||
pop ecx
|
||||
|
||||
:ReadVersion
|
||||
; Parse the version string
|
||||
push v4
|
||||
push v3
|
||||
push v2
|
||||
push v1
|
||||
push :VersionString
|
||||
push gVersionString
|
||||
call sscanf
|
||||
add esp, 18
|
||||
|
||||
mov ecx, bufchk
|
||||
call 480DC1
|
||||
leave
|
||||
retn
|
||||
|
||||
:VersionPath
|
||||
data 25 73 5C 76 65 72 73 69 6F 6E 2E 74 78 74 00 00 ; "%s\version.txt"
|
||||
:VersionString
|
||||
data 76 25 64 2E 25 64 2E 25 64 2E 25 64 00 ; "v%d.%d.%d.%d"
|
||||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 134 B After Width: | Height: | Size: 134 B |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 638 B |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |