add patcher scripts

This commit is contained in:
duncathan 2021-11-30 22:37:50 -06:00
parent 05409023cd
commit bd8e4889fa
4 changed files with 94 additions and 46 deletions

2
.gitignore vendored
View file

@ -8,3 +8,5 @@ pre-edited-cs/Profile*
pre-edited-cs/window\.rect
*Copy/
venv/

60
caver/patcher.py Normal file
View file

@ -0,0 +1,60 @@
from pathlib import Path
from typing import Optional
from lupa import LuaRuntime
import logging
import shutil
pre_edited_cs = __import__("pre-edited-cs")
CSVERSION = 3
def patch_files(patch_data: dict, output_dir: Path):
ensure_base_files_exist(output_dir)
TscFile = LuaRuntime().eval(Path(__file__).parent.joinpath("tsc_file.lua").read_text())
for mapname, mapdata in patch_data["maps"].items():
patch_map(mapname, mapdata, TscFile, output_dir)
patch_mychar(patch_data["mychar"], output_dir)
patch_hash(patch_data["hash"], output_dir)
def ensure_base_files_exist(output_dir: Path):
internal_copy = pre_edited_cs.get_path()
version = output_dir.joinpath("data", "Stage", "_version.txt")
keep_existing_files = version.exists() and int(version.read_text()) >= CSVERSION
def should_ignore(path: Path, names: list[str]):
if not keep_existing_files:
return []
return [path.joinpath(name) for name in names if path.joinpath(name).exists() and path.joinpath(name).is_file()]
shutil.copytree(internal_copy, output_dir, ignore=should_ignore)
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({}, mappath.read_bytes(), logging.getLogger("caver"))
for event, script in mapdata["pickups"].items():
TscFile.placeItemAtLocation(tsc_file, script, event, mapname)
for event, song in mapdata["music"].items():
TscFile.placeSongAtCue(tsc_file, song["song_id"], event, song["original_id"], mapname)
for event, script in mapdata["entrances"].items():
TscFile.placeTraAtEntrance(tsc_file, script, event, mapname)
mappath.write_bytes(TscFile.getText(tsc_file))
output_dir.joinpath("data", "Plaintext", f"{mapname}.txt")
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)
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)

View file

@ -1,49 +1,33 @@
local C = Class:extend()
local TscFile = {}
-- local ITEM_DATA = require 'database.items'
local OPTIONAL_REPLACES = {
'Max health increased by ',
'Max life increased by ',
'<ACH0041', -- Cave Story+ only, trigger achievement.
}
function C:new(path)
logInfo('reading TSC: ' .. path)
local file = lf.newFile(path)
assert(file:open('r'))
local contents, size = file:read()
function TscFile:new(contents, py_logging)
self.log = py_logging
self._text = self:_codec(contents, 'decode')
assert(file:close())
assert(file:release())
end
function C:hasUnreplacedItems()
return #self._unreplaced >= 1
end
function C:placeItemAtLocation(item, location)
function TscFile:placeItemAtLocation(script, event, mapname)
local wasChanged
self._text, wasChanged = self:_stringReplace(self._text, "<EVE....", item.script, location.event)
self._text, wasChanged = self:_stringReplace(self._text, "<EVE....", script, event)
if not wasChanged then
local template = 'Unable to place [%s] "%s" at "%s".'
logError(template:format(location.map, item.name, location.name))
local template = 'Unable to place script "%s" at [%s] event "%s".'
self.log.error(template:format(script, mapname, event))
end
end
function C:placeSongAtCue(songid, event, map, originalid)
function TscFile:placeSongAtCue(songid, event, originalid, mapname)
local wasChanged
self._text, wasChanged = self:_stringReplace(self._text, "<CMU" .. originalid, "<CMU" .. songid, event, {"<CMU0015", "<CMU0000"})
if not wasChanged then
local template = "Unable to replace [%s] event #%s's music cue with %q."
logWarning(template:format(map, event, songid))
self.log.warning(template:format(mapname, event, songid))
end
end
function C:_stringReplace(text, needle, replacement, label, overrides)
function TscFile:placeTraAtEntrance(script, event, mapname)
return -- TODO for entrance rando
end
function TscFile:_stringReplace(text, needle, replacement, label, overrides)
overrides = overrides or {}
local pStart, pEnd = self:_getLabelPositionRange(label)
@ -53,11 +37,11 @@ function C:_stringReplace(text, needle, replacement, label, overrides)
i = text:find(needle, pStart)
if i == nil then
logDebug(('Unable to replace "%s" with "%s"'):format(needle, replacement))
self.log.debug(('Unable to replace "%s" with "%s"'):format(needle, replacement))
return text, false
elseif i > pEnd then
-- This is totally normal and can be ignored.
logDebug(('Found "%s", but was outside of label.'):format(needle, replacement))
self.log.debug(('Found "%s", but was outside of label.'):format(needle, replacement))
return text, false
end
@ -88,7 +72,7 @@ function C:_stringReplace(text, needle, replacement, label, overrides)
return a .. replacement .. b, true
end
function C:_getLabelPositionRange(label)
function TscFile:_getLabelPositionRange(label)
local labelStart, labelEnd
-- Recursive shit for when label is a table...
@ -128,7 +112,7 @@ function C:_getLabelPositionRange(label)
end
if labelStart == nil then
logError(("%s: Could not find label: %s"):format(self.mapName, label))
self.log.error(("%s: Could not find label: %s"):format(self.mapName, label))
labelStart = 1
end
@ -139,18 +123,15 @@ function C:_getLabelPositionRange(label)
return labelStart, labelEnd
end
function C:writePlaintextTo(path)
logInfo('writing Plaintext TSC to: ' .. path)
U.writeFile(path, self._text)
function TscFile:getPlaintext()
return self._text
end
function C:writeTo(path)
logInfo('writing TSC to: ' .. path)
local encoded = self:_codec(self._text, 'encode')
U.writeFile(path, encoded)
function TscFile:getText()
return self:_codec(self._text, 'encode')
end
function C:_codec(text, mode)
function TscFile:_codec(text, mode)
-- Create array of chars.
local chars = {}
text:gsub(".", function(c) table.insert(chars, c) end)
@ -166,9 +147,9 @@ function C:_codec(text, mode)
error('Unknown codec mode: ' .. tostring(mode))
end
logDebug(" filesize", #chars)
logDebug(" encoding char:", encodingChar)
logDebug(" encoding char position:", encodingCharPosition)
self.log.debug(" filesize", #chars)
self.log.debug(" encoding char:", encodingChar)
self.log.debug(" encoding char position:", encodingCharPosition)
-- Encode or decode.
for pos, char in ipairs(chars) do
@ -182,4 +163,4 @@ function C:_codec(text, mode)
return decoded
end
return C
return TscFile

View file

@ -0,0 +1,5 @@
from pathlib import Path
def get_path():
return Path(__file__).parent