mirror of
https://github.com/cave-story-randomizer/cave-story-randomizer
synced 2025-03-21 01:19:19 +00:00
fix patcher functionality (hints TODO)
This commit is contained in:
parent
ea83e7643f
commit
e38dcdb197
|
@ -1,5 +1,5 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Callable, Optional
|
||||||
from lupa import LuaRuntime
|
from lupa import LuaRuntime
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -8,15 +8,27 @@ pre_edited_cs = __import__("pre-edited-cs")
|
||||||
|
|
||||||
CSVERSION = 3
|
CSVERSION = 3
|
||||||
|
|
||||||
def patch_files(patch_data: dict, output_dir: Path):
|
def patch_files(patch_data: dict, output_dir: Path, progress_update: Callable[[str, float], None]):
|
||||||
|
progress_update("Copying base files...", 0.0)
|
||||||
ensure_base_files_exist(output_dir)
|
ensure_base_files_exist(output_dir)
|
||||||
|
|
||||||
TscFile = LuaRuntime().eval(Path(__file__).parent.joinpath("tsc_file.lua").read_text())
|
mapcount = len(patch_data["maps"].keys())
|
||||||
for mapname, mapdata in patch_data["maps"].items():
|
|
||||||
|
lua_file = Path(__file__).parent.joinpath("tsc_file.lua").read_text()
|
||||||
|
try:
|
||||||
|
TscFile = LuaRuntime().execute(lua_file)
|
||||||
|
except Exception as e:
|
||||||
|
print(lua_file)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
for i, (mapname, mapdata) in enumerate(patch_data["maps"].items()):
|
||||||
|
progress_update(f"Patching {mapname}...", i/mapcount)
|
||||||
patch_map(mapname, mapdata, TscFile, output_dir)
|
patch_map(mapname, mapdata, TscFile, output_dir)
|
||||||
|
|
||||||
|
progress_update("Copying MyChar...", 1.0)
|
||||||
patch_mychar(patch_data["mychar"], output_dir)
|
patch_mychar(patch_data["mychar"], output_dir)
|
||||||
|
|
||||||
|
progress_update("Copying hash...", 1.0)
|
||||||
patch_hash(patch_data["hash"], output_dir)
|
patch_hash(patch_data["hash"], output_dir)
|
||||||
|
|
||||||
def ensure_base_files_exist(output_dir: Path):
|
def ensure_base_files_exist(output_dir: Path):
|
||||||
|
@ -25,16 +37,19 @@ def ensure_base_files_exist(output_dir: Path):
|
||||||
version = output_dir.joinpath("data", "Stage", "_version.txt")
|
version = output_dir.joinpath("data", "Stage", "_version.txt")
|
||||||
keep_existing_files = version.exists() and int(version.read_text()) >= CSVERSION
|
keep_existing_files = version.exists() and int(version.read_text()) >= CSVERSION
|
||||||
|
|
||||||
def should_ignore(path: Path, names: list[str]):
|
def should_ignore(path: str, names: list[str]):
|
||||||
if not keep_existing_files:
|
base = ["__init__.py", "__pycache__"]
|
||||||
return []
|
if keep_existing_files:
|
||||||
return [path.joinpath(name) for name in names if path.joinpath(name).exists() and path.joinpath(name).is_file()]
|
p = Path(path)
|
||||||
|
base.extend([p.joinpath(name) for name in names if p.joinpath(name).exists() and p.joinpath(name).is_file()])
|
||||||
shutil.copytree(internal_copy, output_dir, ignore=should_ignore)
|
return base
|
||||||
|
|
||||||
|
shutil.copytree(internal_copy, output_dir, ignore=should_ignore, dirs_exist_ok=True)
|
||||||
|
output_dir.joinpath("data", "Plaintext").mkdir(exist_ok=True)
|
||||||
|
|
||||||
def patch_map(mapname: str, mapdata: dict[str, dict], TscFile, output_dir: Path):
|
def patch_map(mapname: str, mapdata: dict[str, dict], TscFile, output_dir: Path):
|
||||||
mappath = output_dir.joinpath("data", "Stage", f"{mapname}.tsc")
|
mappath = output_dir.joinpath("data", "Stage", f"{mapname}.tsc")
|
||||||
tsc_file = TscFile.new({}, mappath.read_bytes(), logging.getLogger("caver"))
|
tsc_file = TscFile.new(TscFile, mappath.read_bytes(), logging.getLogger("caver"))
|
||||||
|
|
||||||
for event, script in mapdata["pickups"].items():
|
for event, script in mapdata["pickups"].items():
|
||||||
TscFile.placeItemAtLocation(tsc_file, script, event, mapname)
|
TscFile.placeItemAtLocation(tsc_file, script, event, mapname)
|
||||||
|
@ -44,9 +59,13 @@ def patch_map(mapname: str, mapdata: dict[str, dict], TscFile, output_dir: Path)
|
||||||
|
|
||||||
for event, script in mapdata["entrances"].items():
|
for event, script in mapdata["entrances"].items():
|
||||||
TscFile.placeTraAtEntrance(tsc_file, script, event, mapname)
|
TscFile.placeTraAtEntrance(tsc_file, script, event, mapname)
|
||||||
|
|
||||||
|
for event, script in mapdata["hints"].items():
|
||||||
|
TscFile.placeHintAtEvent(tsc_file, script, event, mapname)
|
||||||
|
|
||||||
mappath.write_bytes(TscFile.getText(tsc_file))
|
chars = TscFile.getText(tsc_file).values()
|
||||||
output_dir.joinpath("data", "Plaintext", f"{mapname}.txt")
|
mappath.write_bytes(bytes(chars))
|
||||||
|
output_dir.joinpath("data", "Plaintext", f"{mapname}.txt").write_text(TscFile.getPlaintext(tsc_file))
|
||||||
|
|
||||||
def patch_mychar(mychar: Optional[str], output_dir: Path):
|
def patch_mychar(mychar: Optional[str], output_dir: Path):
|
||||||
if mychar is None:
|
if mychar is None:
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
local TscFile = {}
|
local TscFile = {}
|
||||||
|
|
||||||
function TscFile:new(contents, py_logging)
|
function TscFile:new(contents)
|
||||||
self.log = py_logging
|
o = {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
self._text = self:_codec(contents, 'decode')
|
self._text = self:_codec(contents, 'decode')
|
||||||
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
function TscFile:placeItemAtLocation(script, event, mapname)
|
function TscFile:placeItemAtLocation(script, event, mapname)
|
||||||
|
@ -10,7 +13,7 @@ function TscFile:placeItemAtLocation(script, event, mapname)
|
||||||
self._text, wasChanged = self:_stringReplace(self._text, "<EVE....", script, event)
|
self._text, wasChanged = self:_stringReplace(self._text, "<EVE....", script, event)
|
||||||
if not wasChanged then
|
if not wasChanged then
|
||||||
local template = 'Unable to place script "%s" at [%s] event "%s".'
|
local template = 'Unable to place script "%s" at [%s] event "%s".'
|
||||||
self.log.error(template:format(script, mapname, event))
|
error(template:format(script, mapname, event))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -19,7 +22,7 @@ function TscFile:placeSongAtCue(songid, event, originalid, mapname)
|
||||||
self._text, wasChanged = self:_stringReplace(self._text, "<CMU" .. originalid, "<CMU" .. songid, event, {"<CMU0015", "<CMU0000"})
|
self._text, wasChanged = self:_stringReplace(self._text, "<CMU" .. originalid, "<CMU" .. songid, event, {"<CMU0015", "<CMU0000"})
|
||||||
if not wasChanged then
|
if not wasChanged then
|
||||||
local template = "Unable to replace [%s] event #%s's music cue with %q."
|
local template = "Unable to replace [%s] event #%s's music cue with %q."
|
||||||
self.log.warning(template:format(mapname, event, songid))
|
error(template:format(mapname, event, songid))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,6 +30,10 @@ function TscFile:placeTraAtEntrance(script, event, mapname)
|
||||||
return -- TODO for entrance rando
|
return -- TODO for entrance rando
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function TscFile:placeHintAtEvent(script, event, mapname)
|
||||||
|
return -- TODO
|
||||||
|
end
|
||||||
|
|
||||||
function TscFile:_stringReplace(text, needle, replacement, label, overrides)
|
function TscFile:_stringReplace(text, needle, replacement, label, overrides)
|
||||||
overrides = overrides or {}
|
overrides = overrides or {}
|
||||||
local pStart, pEnd = self:_getLabelPositionRange(label)
|
local pStart, pEnd = self:_getLabelPositionRange(label)
|
||||||
|
@ -37,11 +44,11 @@ function TscFile:_stringReplace(text, needle, replacement, label, overrides)
|
||||||
i = text:find(needle, pStart)
|
i = text:find(needle, pStart)
|
||||||
|
|
||||||
if i == nil then
|
if i == nil then
|
||||||
self.log.debug(('Unable to replace "%s" with "%s"'):format(needle, replacement))
|
-- print(('Unable to replace "%s" with "%s"'):format(needle, replacement))
|
||||||
return text, false
|
return text, false
|
||||||
elseif i > pEnd then
|
elseif i > pEnd then
|
||||||
-- This is totally normal and can be ignored.
|
-- This is totally normal and can be ignored.
|
||||||
self.log.debug(('Found "%s", but was outside of label.'):format(needle, replacement))
|
-- print(('Found "%s", but was outside of label.'):format(needle, replacement))
|
||||||
return text, false
|
return text, false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -112,7 +119,7 @@ function TscFile:_getLabelPositionRange(label)
|
||||||
end
|
end
|
||||||
|
|
||||||
if labelStart == nil then
|
if labelStart == nil then
|
||||||
self.log.error(("%s: Could not find label: %s"):format(self.mapName, label))
|
error(("%s: Could not find label: %s"):format(self.mapName, label))
|
||||||
labelStart = 1
|
labelStart = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -147,20 +154,23 @@ function TscFile:_codec(text, mode)
|
||||||
error('Unknown codec mode: ' .. tostring(mode))
|
error('Unknown codec mode: ' .. tostring(mode))
|
||||||
end
|
end
|
||||||
|
|
||||||
self.log.debug(" filesize", #chars)
|
|
||||||
self.log.debug(" encoding char:", encodingChar)
|
|
||||||
self.log.debug(" encoding char position:", encodingCharPosition)
|
|
||||||
|
|
||||||
-- Encode or decode.
|
-- Encode or decode.
|
||||||
for pos, char in ipairs(chars) do
|
for pos, char in ipairs(chars) do
|
||||||
if pos ~= encodingCharPosition then
|
if pos ~= encodingCharPosition then
|
||||||
local byte = (char:byte() + encodingChar) % 256
|
local byte = (char:byte() + encodingChar) % 256
|
||||||
chars[pos] = string.char(byte)
|
if mode == 'decode' then
|
||||||
|
chars[pos] = string.char(byte)
|
||||||
|
else
|
||||||
|
chars[pos] = byte
|
||||||
|
end
|
||||||
|
elseif mode == 'encode' then
|
||||||
|
chars[pos] = char:byte()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local decoded = table.concat(chars)
|
if mode == 'encode' then
|
||||||
|
return chars
|
||||||
return decoded
|
end
|
||||||
|
return table.concat(chars)
|
||||||
end
|
end
|
||||||
|
|
||||||
return TscFile
|
return TscFile
|
||||||
|
|
Loading…
Reference in a new issue