Merge pull request #170 from cave-story-randomizer/patcher

merge patcher branch into main
This commit is contained in:
duncathan salt 2023-06-19 16:51:07 -06:00 committed by GitHub
commit c9c4bf1e10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
888 changed files with 567 additions and 17493 deletions

65
.github/workflows/python.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: Python Package
on:
push:
branches:
- '*'
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
name: Wheel
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: 'recursive'
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.9"
- name: Install Python packages
run: python -m pip install --upgrade build pip
- name: build wheel
run: python -m build --wheel
- name: build sdist
run: python -m build --sdist
- name: Store the packages
uses: actions/upload-artifact@v2
with:
name: python-package-distributions
path: dist
pypi:
runs-on: 'ubuntu-latest'
needs:
- build
steps:
- name: Download all the dists
uses: actions/download-artifact@v2
with:
name: python-package-distributions
path: dist/
- name: Publish 📦 to TestPyPI
if: ${{ github.ref == 'refs/heads/patcher' }}
uses: pypa/gh-action-pypi-publish@master
with:
password: ${{ secrets.testpypi_password }}
repository_url: https://test.pypi.org/legacy/
- name: Publish 📦 to PyPI
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: pypa/gh-action-pypi-publish@master
with:
password: ${{ secrets.pypi_password }}

17
.gitignore vendored
View File

@ -1,10 +1,21 @@
data/*
notes/*
pre-edited-cs/Doukutsu\.exe\.blbkp
pre_edited_cs/Doukutsu\.exe\.blbkp
pre-edited-cs/Profile*
pre_edited_cs/Profile*
pre-edited-cs/window\.rect
pre_edited_cs/window\.rect
*Copy/
venv/
build/
dist/
cave_story_randomizer.egg-info/
*/__pycache__
caver/version.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

View File

@ -1,52 +1,3 @@
# Cave Story Randomizer [Open Mode]
The Cave Story Randomizer shuffles the location of every item in Cave Story, creating a new experience each time you play! The randomizer has logic in place to ensure that you can always reach every item and finish the game. Get started by heading to the [releases page](https://github.com/cave-story-randomizer/cave-story-randomizer/releases) and downloading the most recent version! If you find yourself stuck, wanting to talk about the hilarious location you found the panties in, or just plain enjoying the game please consider joining our [official Discord server](https://discord.gg/7zUdPEn) and hanging out!
## Main differences
Note that there are a few key differences from the vanilla game in order to improve the playing experience:
- All 5 teleporter locations in Arthur's House are active from the beginning of the game
- All other teleporters from the vanilla game are active and linked to one another at all times
- A teleporter between Sand Zone (near the Storehouse) and Labyrinth I has been placed and can be activated in one of two ways:
1. Defeating Toroko+
2. Using the teleporter from the Labyrinth I side
- Most cutscenes have been abridged or skipped entirely
- Jellyfish Juice can be used an infinite number of times
- You can carry as many as 5 puppies at once: Jenka will only accept them once you've collected all 5
- By the way, all 5 puppies will be located somewhere in the Sand Zone
- Certain items that are received from NPCs have been placed in chests:
- Labyrinth B (Fallen Booster)
- Labyrinth Shop
- One requiring the Machine Gun to open
- One requiring the Fireball to open
- One requiring the Spur to open
- Jail no. 1
- Storage? (Ma Pignon)
- This chest requires saving Curly in the Waterway to open
- If you don't have Curly's Air Tank after defeating the Core, the water will not rise and you may leave without dying
- Curly cannot be left behind permanently in the Core; the shutter will never close once the boss has been defeated
- The jump in the Waterway to save Curly has been made much easier
- Ironhead will always give you his item on defeat (but there's still a special surprise if you defeat him without taking damage!)
- Kazuma will only open the door between Egg no. 0 and the Outer Wall if you save him in Grasstown
- Kazuma's door can be blown down from both the outside and the inside
- Entering the Throne Room to complete the game requires doing three things:
1. Saving Sue in the Egg Corridor
2. Obtaining the Booster 2.0
3. Obtaining the Iron Bond
## Help me!
If you find yourself stuck, here are a few common pitfalls:
- Remember that the Jellyfish Juice can quench more than one fireplace
- The Graveyard can only be accessed if you obtain the Silver Locket and see Toroko get kidnapped
- The Hermit Gunsmith will wake up and give you an item if you defeat the Core and show him his gun
- The western side of the Labyrinth can be accessed without flight if you defeat Toroko+
- The Plantation can be accessed without the Teleporter Room Key if you save Kazuma and teleport in or climb the Outer Wall
- The Waterway can be accessed without the Cure-All by using the teleporter in the Labyrinth Shop
- There may be a required item in the Last Cave (Hidden) as a reward for defeating the Red Demon
If you're still stuck, join our [official Discord server](https://discord.gg/7zUdPEn) and ask for help in there!
## Credits
- Original Cave Story Randomizer by shru: https://shru.itch.io/cave-story-randomizer
- Font: https://datagoblin.itch.io/monogram
- Icon: Bubbler (@Ethan#6397)
A patcher for randomizing Cave Story. If you want to play, check out [Randovania](https://github.com/randovania/randovania)!

18
__pyinstaller/__init__.py Normal file
View File

@ -0,0 +1,18 @@
import os
# Functions
# =========
#
# .. _get_hook_dirs:
#
# get_hook_dirs
# -------------
#
# Tell PyInstaller where to find hooks provided by this distribution;
# this is referenced by the :ref:`hook registration <hook_registration>`.
# This function returns a list containing only the path to this
# directory, which is the location of these hooks.
def get_hook_dirs():
return [os.path.dirname(__file__)]

View File

@ -0,0 +1,5 @@
from PyInstaller.utils.hooks import collect_data_files
# https://pyinstaller.readthedocs.io/en/stable/hooks.html#provide-hooks-with-package
datas = collect_data_files('caver', excludes=['__pyinstaller'])

View File

@ -0,0 +1,5 @@
from PyInstaller.utils.hooks import collect_data_files
# https://pyinstaller.readthedocs.io/en/stable/hooks.html#provide-hooks-with-package
datas = collect_data_files('pre_edited_cs', excludes=['__pyinstaller'])

126
caver/patcher.py Normal file
View File

@ -0,0 +1,126 @@
from pathlib import Path
from typing import Callable, Optional
from lupa import LuaRuntime
import logging
import shutil
import textwrap
import sys
import pre_edited_cs
CSVERSION = 4
class CaverException(Exception):
pass
def get_path() -> Path:
if getattr(sys, "frozen", False):
file_dir = Path(getattr(sys, "_MEIPASS"))
else:
file_dir = Path(__file__).parent.parent
return file_dir.joinpath("caver")
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(output_dir)
total = len(patch_data["maps"].keys()) + len(patch_data["other_tsc"].keys()) + 2
lua_file = get_path().joinpath("tsc_file.lua").read_text()
TscFile = LuaRuntime().execute(lua_file)
for i, (mapname, mapdata) in enumerate(patch_data["maps"].items()):
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)
patch_other(filename, scripts, TscFile, output_dir)
i += 1
progress_update("Copying MyChar...", i/total)
patch_mychar(patch_data["mychar"], output_dir)
i += 1
progress_update("Copying hash...", i/total)
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: str, names: list[str]):
base = ["__init__.py", "__pycache__", "ScriptSource", "__pyinstaller"]
if keep_existing_files:
p = Path(path)
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, 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")
output_dir.joinpath("data", "Plaintext").mkdir(exist_ok=True)
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"))
for event, script in mapdata["pickups"].items():
TscFile.placeScriptAtEvent(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():
needle = "<EVE...." # TODO: create a proper pattern
TscFile.placeScriptAtEvent(tsc_file, script, event, mapname, needle)
for event, hint in mapdata["hints"].items():
script = create_hint_script(hint["text"], hint.get("facepic", "0000") != "0000", hint.get("ending", "<END"))
TscFile.placeScriptAtEvent(tsc_file, script, event, mapname)
chars = TscFile.getText(tsc_file).values()
mappath.write_bytes(bytes(chars))
output_dir.joinpath("data", "Plaintext", f"{mapname}.txt").write_text(TscFile.getPlaintext(tsc_file))
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"))
for event, script in scripts.items():
TscFile.placeScriptAtEvent(tsc_file, script["script"], event, filename, script.get("needle", "<EVE...."))
chars = TscFile.getText(tsc_file).values()
filepath.write_bytes(bytes(chars))
output_dir.joinpath("data", "Plaintext", f"{filename}.txt").write_text(TscFile.getPlaintext(tsc_file))
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)
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.
"""
hard_limit = 35
msgbox_limit = 26 if facepic else hard_limit
lines = textwrap.wrap(text, width=msgbox_limit, max_lines=3)
text = ""
for i, l in enumerate(lines):
text += l
if len(l) != hard_limit and i < len(lines)-1:
text += "\r\n"
return f"<PRI<MSG<TUR{text}<NOD{ending}"

View File

@ -1,64 +1,48 @@
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)
o = {}
setmetatable(o, self)
self.__index = self
self._text = self:_codec(contents, 'decode')
assert(file:close())
assert(file:release())
return o
end
function C:hasUnreplacedItems()
return #self._unreplaced >= 1
end
function C:placeItemAtLocation(item, location)
local wasChanged
self._text, wasChanged = self:_stringReplace(self._text, "<EVE....", item.script, location.event)
if not wasChanged then
local template = 'Unable to place [%s] "%s" at "%s".'
logError(template:format(location.map, item.name, location.name))
function TscFile:placeScriptAtEvent(script, event, mapname, needle)
needle = needle or "<EVE...."
local err
self._text, err = self:_stringReplace(self._text, needle, script, event)
if err ~= nil then
local template = 'Unable to place script "%s" at [%s] event "%s".\nCause: %s'
error(template:format(script, mapname, event, err))
end
end
function C:placeSongAtCue(songid, event, map, originalid)
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))
function TscFile:placeSongAtCue(songid, event, originalid, mapname)
local err
self._text, err = self:_stringReplace(self._text, "<CMU" .. originalid, "<CMU" .. songid, event, {"<CMU0015", "<CMU0000"})
if err ~= nil then
local template = "Unable to replace [%s] event #%s's music cue with %q.\nCause: %s"
error(template:format(mapname, event, songid, err))
end
end
function C:_stringReplace(text, needle, replacement, label, overrides)
function TscFile:_stringReplace(text, needle, replacement, label, overrides)
overrides = overrides or {}
local pStart, pEnd = self:_getLabelPositionRange(label)
local i, o = -1, -1
while(o <= i) do
o = nil
i = text:find(needle, pStart)
i, i2 = text:find(needle, pStart)
if i == nil then
logDebug(('Unable to replace "%s" with "%s"'):format(needle, replacement))
return text, false
local err = ('No match for "%s".'):format(needle)
return text, err
elseif i > pEnd then
-- This is totally normal and can be ignored.
logDebug(('Found "%s", but was outside of label.'):format(needle, replacement))
return text, false
local err = ('Found "%s", but was outside of label (%d, %d) at index %d.'):format(needle, pStart, pEnd, i)
return text, err
end
-- find the earliest occurence of an override
@ -79,16 +63,16 @@ function C:_stringReplace(text, needle, replacement, label, overrides)
pStart = o+1
end
local len = needle:len()
local len = i2-i+1
local j = i + len - 1
assert((i % 1 == 0) and (i > 0) and (i <= j), tostring(i))
assert((j % 1 == 0), tostring(j))
local a = text:sub(1, i - 1)
local b = text:sub(j + 1)
return a .. replacement .. b, true
return a .. replacement .. b, nil
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))
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,20 +147,23 @@ function C:_codec(text, mode)
error('Unknown codec mode: ' .. tostring(mode))
end
logDebug(" filesize", #chars)
logDebug(" encoding char:", encodingChar)
logDebug(" encoding char position:", encodingCharPosition)
-- Encode or decode.
for pos, char in ipairs(chars) do
if pos ~= encodingCharPosition then
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
local decoded = table.concat(chars)
return decoded
if mode == 'encode' then
return chars
end
return table.concat(chars)
end
return C
return TscFile

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
-::C:FWXKFMW_::::FPKS::::FOXN-::C;FWXKFMW_::::FPKS:::;FOXN-::C<FWXKFMW_::::FPKS:::<FOXN-::C=FWXKFMW_::::FPKS:::=FOXN-::C>FWXKFMW_::::FPKS:::>FOXN-::C?-::CCFUOcFWXKFMW_::::FKXZ:;?:D:::;D::::FPKS:::>FMXZ:=:;D:;;<D:::<FaKS:;::FKXZ:;?:D::::D::::FWcN:::<FNXZ:=:;F]WMFOXN-:;::FZ\SF]K^FPVT;:<=D:;:<FS^T::<?D:;:;FW]QS~1}*vymuon8FXYNFOXN-:;:;F]Y_::<<FW]QF^_\FQS^;:<?_}on*~ro*G^ovozy|~o|*\yyw*Uoエ8FXYNFMVYFS^7::<?FQS^::::FPV5;:<=FO`O:;:<-:;:<FUOcF]Y_::;;FKXZ:;::D::::D:::<FPKY:::<F^\K::?@D::C<D:::;D:;:=
-:;?:FUOcF]K^FPVT;;>:D:;?<FS^T::<?D:;?;FPVT;:<=D:;?;FO`O:;?<-:;?;FUOcFW]QNy*ペ*x~*~y*qy*~y*K|~r|1}ry}oIFcXT::::FMVYFPV5;:B<FPV5;;>:FZ]5:::?D@::?FWcN:::<FKXZ:<?:D::;:D::::FaKS::@:FMXZ:<@:D:;?:D::::FRWMFKXZ:<@:D::;:D::::FaKS::?:FPKY:::=FaKS:;::F]WMFPVT;;>;D:;?<FPV5;;>;F^\K::?AD:<<:D::;;D::;>-:;?<FZ\SFW]QNy*ペ*x~*~y*qy*~y*K|~r|1}ry}oIFcXT::::FMVYFZ]5:::?D@::?FUOcFMXZ:=::D:;;;D:::<FRWMFaKS::@:FKXZ:;?:D:::;D::::FaKS:;::FPKY:::>F^\K:::;D::CCD:::?D:::B-:<?:FUOcFW]Q88888FXYNFOXN-:<@:-:=::-:=:;

View File

@ -1 +0,0 @@
0-FSS\S0-_pqd_fpxSSSS_idlSSSS_hqg0-FSS\T0-_pqd_fpxSSSS_idlSSST_hqg0-FSS\U0-_pqd_fpxSSSS_idlSSSU_hqg0-FSS\V0-_pqd_fpxSSSS_idlSSSV_hqg0-FSS\W0-_pqd_fpxSSSS_idlSSSW_hqg0-FSS\[0-_dpmSSSU]SSSS_dpmSSSW]SSSS_dpmSSSX]SSSS0-_dpmSSS\]SSSS_dpmSSTS]SSSS_dpmSSTU]SSSS_dpmSSTV]SSSS0-_fqsSXSS]SUTY]SSSS_pqsSXSS]SSSY]SSS\]SSSS_hqg0-0-FSTSS0-_sul_vrxSSTT_gqsSTSS_idrSSSW_wudSSU\]SS\W]SSSY]SSS\0-#SWSS0-_sul_vrxSSUU_gqsSWSS_hyhSWST0-FSWST0-_hyhSSUS0-FSWUS0-_nh|_gqsSWUS_hyhSWUT0-FSWUT0-_hyhSS[X0-FSXSS0-_pvj_wxuo鋳事C曙肢C恍労譜C倦CГ東漕飢Cz圏<7A>0-恍<>C嚠<43>酎飽C恍呂酔葛佑<E8919B>C沫<43>参_qrg0-j辰<6A><E8BEB0>C嵐C雷<43><EFBFBD>傭_zdlSSWS_|qmSSSS_for0-_olNTSSS_vrxSSUS_dhN0-_iomYUST]SXST0-_iomYUSU]SXSU0-_wudSSTV]SS\T]SSTS]SSS[0-0-FSXST0-_wudSSST]SS\W]SSS[]SSSW0-0-FSXSU0-_wudSSWS]SS\W]SSTW]SSS\0-

View File

@ -1 +0,0 @@
|y挓煥焲y<E784B2><EFBFBD>寄煙牗<E78599>案煙煙<E78599>匠|y挓煥爘y<E78898><EFBFBD>寄煙牗<E78599>案煙煚<E78599>匠|y挓煥<E68C93>y<EFBFBD><EFBFBD>寄煙牗<E78599>案煙煛<E78599>匠|y挓煥<E68C93>y<EFBFBD><EFBFBD>寄煙牗<E78599>案煙煝<E78599>匠|y挓煥<E68C93>y<EFBFBD><EFBFBD>寄煙牗<E78599>案煙煟<E78599>匠|y挓煥<E68C93>y<EFBFBD>脊煙煛<E78599>煙煫凹篃煙煙煙|y<>脊煙煥<E78599>煙煫凹篃煚々煙煙<E78599>脊煙牏<E78599>煙焲y<E784B2>娇煠煙<E785A0><E78599>ォ煙煙<E78599>娇煠煙<E785A0>煚ォ煙牊<E78599>煙煫唇硘y|y挓牊焲y<E784B2><EFBFBD>灸煙牋<E78599>娇煚煙<E7859A>熬煙煟<E78599>涟煙牊<E78599>煙ⅳ<E78599><EFBFBD>y挓<79>焲y<E784B2><EFBFBD><EFBFBD>安煙牏谱迯嗅詮柁錆潩<E98C86>骄硘y笘逶忀藻葬忊栽輳柁鋦y嗅掬萦徰哉掎詽<E68E8E>境|y刚忚掬徰嶝葜徿詮荑|y肇逶彨陡脿煚夸哌卦猬洬骄倡不莲磁礋<E7A381>爘y挓<79>爘y<E78898>糯煙煙|y挓<79>焲y<E784B2><EFBFBD><EFBFBD>安煙牏裸刿蹚辙溽徾哉銤潩<E98AA4>境|y厕溘訌阕剽忁塾忔捃休|y汜掬眼詮柁鋸戕徴剌訌自醸y捭自釓咪哌卦鈴辙釓自岙<E887AA><EFBFBD>匠|y挓<79>爘y<E78898><EFBFBD><EFBFBD>安煙牏米嵩詮苻嵩徾哉銤<E59389>境|y掇抻徾湟跐<E6B99F><EFBFBD>匠|y挓<79><E68C93>y<EFBFBD><EFBFBD><EFBFBD>安煙牏笍阕剌趶荑徲孕嶝遭|y仔逶忋汹暂徯徾刳剌謡y戕忚掬潾骄倡不撩孓徿掎詮戕徶逎潩<E9808E><EFBFBD>匠|y挓<79><E68C93>y<EFBFBD><EFBFBD><EFBFBD>安煙牏据詮苻嵩忋迯洲潩潾骄硘y笍嵩雄坭忔剽讖柁鋸孓溘觸y醒休愚輳阕詮嵩訌折捩葬鉂<E891AC><EFBFBD>匠|y挓 焲y<E784B2><EFBFBD><EFBFBD>安煙牏艰徿铔徸捩忇湄亿潾骄倡不莲陡脿煚仔銖剽徹萦栽訌掭詮拚|y荑徲孕釓咪哌卦鉂<E58DA6><EFBFBD>涣|y<>该煙煙<E78599>娇煝煚<E7859D>牏煩煙煙<E78599>娇煟煚<E7859F>安煙煙缎逶徸葬忋自彫夸哌璎潾骄倡不莲祷湡ˇ蛋矡煚⒎苘軡潩<E8BBA1>境|y绒鋿嵩徯忊捋迂葬徴徂軀y阕詮怃嵴幸詻徯嵩輺銖柁錆<E69F81><EFBFBD>涣潩潩潾骄倡不粮銖嫘廨栥徾掭謴兄迯阕秀|y卒萦嵩逾忁諒徂艳汊徾刳詜y柁鋸倚茉忋迯阕剽徹廑休訚<E4BC91>境|y厕漭阚遭鈴钾茇中鈴咴嶝庾杂|y秀忋自蒯徸休逾潾骄硘y扳徲赜忋自徸滠休鈴孀迀y汜卦訌戕忂徂阍毅忋自軡<E887AA><EFBFBD>涣厕彷葬杂洀阕詮钾茇中鈢y肇菪圹鑿剌衷忏杂忋自|y辙嵫赜釉輳嵩訌折捩葬鉀<E891AC>境|y休訌剌忋自蒯徿溽釉徂溻|y嵝衷徰灾休忋迯忏嶝谠|y研亿徯中剌忏忋自忈扪捭鉂<E68DAD><EFBFBD>涣沸訌阕詮嵩訌折捩葬鈴蒉銃y早剽阍記忋自徏剀刂锈徿刂足|y仔逶忓休剽自訌暂阖嵩坭<E5B5A9>境|y蔗捃忋自徹廑休訚潩<E8A89A><EFBFBD>涣米嵩詮苻嵩徾哉銤<E59389>境掇抻徾湟跐<E6B99F><EFBFBD>粴煠<E7B2B4><E785A0>匠|y挓 爘y<E78898><EFBFBD><EFBFBD>安煙牏巨拮潾骄倡不莲陡脿煚遭洀柙鉂<E69F99>境|y米秀徹鈴以徙胸蒇鑿掭詮拚|y荑徲嗅圬葜忂溥哓遭潾骄倡不羭y<E7BEAD>该煙煙<E78599>娇煝煛<E7859D>牏煩煙煙<E78599>娇煟煚<E7859F>安煙煙缎逶徸葬忋自彫夸哌璎潾骄倡不莲祷湡ˇ蛋矡煚⒚仔葳忚掬徻剌盂铦|y圃栣詮溥忋o忋揍栽忀捩潩潾骄倡不翝潩潩<E6BDA9><EFBFBD>涣沸逶忚掬徳逶釓庠暂徯|y蔗暂樨杂徏剀刂挟<E58882><EFBFBD>涣米詮肄彷葬杂徏剀刂锈徸捱杂|y阕秀忋自忈杂徴坜嬖徕|y孓溘訌轴休銖阕攒忊汜暂帚诐<E5B89A>境|y米澡徯阍忋自軟徯萦忋自輡y阕澡徴掬肿銖阕詮徂艳汊潾骄倡不羭y忿嬖逶釢潩<E987A2><EFBFBD>涣侈忚掬徻蒉鎻孀秀|y仔哌暂杂忋迯阕攒|y姓阍徭嗅逾<E59785>骄倡不翝潩潩<E6BDA9><EFBFBD>涣搞徹鈴袕庑訌阈墼潩潾骄硘y米詮暂嵝衷訌钾茇中鉀|y圬谠坭忎菪眼詮戕徱掭汜捋|y阕载釓捩輳幸阖掭鉀<E68EAD>境|y迂庑哌孕嵩訌尕阕掬銖衸y汜幸詽<E5B9B8>境|y刚忈滠掎鈴嗅詮戕徰詜y汜溻阍記忋自鑿釉庖暂釉觸y戕忋自忊溽招以洬骄硘y孀葬詮卒苄葩徾劐詽<E58A90><EFBFBD>涣拜徯彳鑿拚徴葬抟剞溻|y钾茇中鈴坜掴杂忎咿輡y阕詮孕徙诐潩<E8AF90>境|y刚忋仔銖嬖嵩忋徜詽潩<E8A9BD><EFBFBD>涣侈忚掬忎萦葬忏休訌蒉娈<E89289>境|y侈忚掬忎萦葬忏休訌邹鎩y有葜葬掬鈴阕詮嵩觸y折捩葬鈴嗅援<E59785><EFBFBD>涣潩潩潾骄倡不撩孓徿掎詮戕徶逎潩<E9808E><EFBFBD>粴煠ā<E785A0>匠|y挓 <E68C93>y<EFBFBD><EFBFBD><EFBFBD>安煙牏米澡忊栽軓戕徺溻銃y杏掎詮柁錆<E69F81>境|y艰忂溥哓遭徲掭栥忔锈阍|y阕载釓姓赵毅剞輳掭|y研訌廪徙鉂<E5BE99><EFBFBD><EFBFBD>该牊牐夹蓁忋仔葳鉂潩<E98982><EFBFBD>涣|y<>该煙煙<E78599>安煙煙缎逶徸葬忋自彫夸哌璎潾骄倡不莲步繜<E6ADA5>ⅸ煚<E285B8><E7859A>煙煫辰繜<E8BEB0>牜祷湡ˇ<E6B9A1>y<EFBFBD>安煙牏潩潩潾骄倡不寥掬栣詮姓阍釓阕詜y忏掎宰掬庠徻澡洀嗅暂栥|y柁錆<E69F81>境|y谱葬载輳圬詮阕詮庠杂鈴戕|y阕詮嵩訌折捩葬鉂潩<E98982><EFBFBD>涣笍愚輺銖倚嵩忔邹忚掬|y嗅詻忚掬栣詮蒉銖衷沣剌謡y阕詮谠铦<E8B0A0>境|y棉忁咴輳阕詮忏掎宰掬庠|y孓溘訌剌遑阍忋仔銖庑茉|y汜兄杂鑿雄蹚掊葬徯中剌潩潾骄倡不辆菰洀沔逈忋揍栽潩潾骄倡不撩自嵩栤忁蒇鑿掭詜y墼浙潾骄倡祷殶え<E6AEB6>唇硘y挓<79>焲y<E784B2>还煠<E8BF98><E785A0><EFBFBD>煫唇硘y挓<79>爘y<E78898>还煠<E8BF98><E785A0><EFBFBD>牜唇硘y挓<79><E68C93>y<EFBFBD>还煠ā<E785A0><C481>唇硘y挓<79><E68C93>y<EFBFBD>还煠á<E785A0><C3A1><EFBFBD>唇硘y挓<79><E68C93>y<EFBFBD>还煠ǎ<E785A0><C78E>唇硘y挓<79>焲y<E784B2><EFBFBD>露搬諓<E690AC><EFBFBD>匠|y挓<79>爘y<E78898><EFBFBD>露搬諒嗅諓<E59785><EFBFBD>匠|y挓<79><E68C93>y<EFBFBD><EFBFBD>露搬諒嗅諒嗅諓<E59785><EFBFBD>匠|y挓<79><E68C93>y<EFBFBD><EFBFBD>露鞭揶邼<E68FB6><EFBFBD>匠|y挓<79><E68C93>y<EFBFBD><EFBFBD>露地逶惈骄倡唇硘y挓<79>焲y挓<79>爘y挓<79>焲y<E784B2><EFBFBD>牧晦挹鈴圬谠忚掬栣詮剌徯忂剌易潖曝阕|y柁溽忔孕咿葩洀柁鋸茇肿銖言忊沅亿潾骄硘y掇徰幸趶戕忋自忊阈徙<E99888>瓢笩煟煫冉篃煙煫不緗y<E7B797>笟牊煙<E7898A>灸煙<E781B8><E78599>礆|y<>还ァ煚<E382A1><E7859A>爘y<E78898>还ァ煛<E382A1><E7859B><EFBFBD>y<EFBFBD>涟煙牏<E78599>煥牘煙牊<E78599><EFBFBD>y|y挓<79>爘y<E78898>涟煙煚<E78599>煙煣<E78599><EFBFBD>y|y挓<79><E68C93>y<EFBFBD>涟煙<E6B69F><E78599>煙牐<E78599>煙▅y

View File

@ -1 +0,0 @@
ro垥暈時o肠〃埠晻晽Ξ晻晻—畅ro垥暈杛o肠〃埠晻晽Ξ晻晼—畅ro垥暈梤o肠〃埠晻晽Ξ晻晽—畅ro垥暈榬o肠〃埠晻晽Ξ晻晿—畅ro垥暈檙o肠〃埠晻晽Ξ晻暀—畅ro垥枙時o“<EFBFBD>「春晻枛々车晼晻Υ晻晽」乏晻殯煏暈棢晻棛煏暆梤o垥棔時o“<EFBFBD>ro悲枙棔煏棔杛o—华晽灋ro垥棔杛o“<EFBFBD>ro悲枛棖煏棔榬o汞晻棟煏棔梤o脯雇守蕝谱蕝卣孜有咽棕摗炒﹔o雇兽吳资菩吰蜒呝褪呝我蕬呣在呚适摗炒﹔o冈呠蕦凼吰衍妻貐淘儋茀推凼呇再豶o运呌受呍邮貐资粕迵穿—畅ro垥棔梤o“<EFBFBD>脯」悍睈枛棖‖<EFBFBD>枙棟箳晻棟<EFBFBD>由噬呍凼讌偻蕝ⅶ自惺觬o刚孜有咽注摗炒<EFBFBD>┏禃棟暋<EFBFBD>獣棔檙o垥棔榬o“<EFBFBD>脯」悍痹孕貐盐惺呠蕝推蓞馅刭吺釉谔蛂o卣孜有咽棕摀摗炒<EFBFBD><EFBFBD>﹔o垥棔檙o—华晻湠ro垥棖時o“<EFBFBD>ro悲枙槝煏棖梤o悲枙棔煏棖杛o—华晽灋ro垥棖杛o<EFBFBD>屫呝宗蕝偻瀑呑郧再貐俗砸ro偻蕝刳姿迫蕝推凼吰儋迫惺蓃o偻呜呂匮朴蓞斡呝褪呎曝贀穿roτ蓞畢杏攒呉朴迏运呞貐苁资ro形蜒噬摀摗炒﹔oи賲卦沂吰以犹呝驮厥呑郧再豶o谟墒棕僭陨呚帐嗜蛽穿ro畢鞘盐售蕝偻兽憛僭詰呁粕呚在沿摀摗炒<EFBFBD><EFBFBD>﹔o垥棖梤o脯ㄚ棕e呝推賲┰荣宰啞炒<EFBFBD>ū珐灼烫斡虆偻瀑呎栽讌拊谟虆韧窝蓃o运藚芪偻呁我啞炒﹔o赐憛嗡呍友迏畢苁资吳谫吰吳钨ro拊谟淌讚摀穿ro岗韧吰呚推沂摀摗炒<EFBFBD><EFBFBD>﹔o垥棗時o“<EFBFBD>悲枙棔煏棗杛o—华晽灋ro垥棗杛o脯畢稍訉賲鞘盐售蕝茀茉咨呝推賠o┰荣宰呚妻貑穿〃狈吩窃儇<EFBFBD>侈仆啞炒﹔o蔽惺呝推賹貐刳照载噬呝詤厝谱蕝沂摗炒<EFBFBD><EFBFBD>﹔o垥棙時o“<EFBFBD>悲枙棔煏棙杛o—华晽灋ro垥棙杛o脯畬覅卦吺萃期刭噬摀摗炒<EFBFBD><EFBFBD>﹔o垥棜時o「<EFBFBD><EFBFBD>晶晻晻脯赐憛褪迵穿〃狈<EFBFBD>凼訉賲苁呉寿吳仕宰胜穿ro臼仆憛瞧刃吰賲偻蕝参椅唐ro畚蜒铺蕮穿ro冈沂杂蕝绕蜒噬厙参厥邹噮赜瀑韧噬ro沂呞諈朴蓞亲在掏賲沂呁首蕮穿roи賲移討叜呁瀑蕝似滓斡虛穿〃狈畢芷貐馅刭呁势晌犹呍谫呝詤淘ro宋赝斡虆嗽讌茀韧朴淌呍藚掌仁摗炒<EFBFBD>ū珐詤拊趨盐惺呝詤宋赝ぁ炒﹔o际蜒憛厥蕝拊趨谱在由呇瀑首摗炒<EFBFBD>ū磖oˇ车晽殨煏晼暉晻晽〖Ξ晻槙京晻晽〖Ξ晻潟roˇ车晽殨煏晻暉晻晽〖Ξ晻棔roˇ车晽殨煏晽暉晻晽ˇ车晼晻煏晻暉晻晽「春晻枛〖Ξ晻棔ro々车晽殨ˇ车晼晻煏晻暉晻晻「春晻枛睈枛棔睈枙棖—畅ro垥棟時o垥棡瀝o〉樊脯摀摀摗炒<EFBFBD><EFBFBD><EFBFBD>

View File

@ -1 +0,0 @@
;8Q^^g^;8j{|ojq{タ^^^jtow^^^^js|r;8Q^^g_;8j{|ojq{タ^^^jtow^^^_js|r;8Q^^g`;8j{|ojq{タ^^^jtow^^^`js|r;8Q^^ga;8j{|ojq{タ^^^jtow^^^ajs|r;8Q^^gb;8j{|ojq{タ^^^jtow^^^bjs|r;8;8Q^_^^;8jys㌦tzx_cdbh^_^_j±タ^__jo|~^_^^h^^^^h^^^`;8jto}^^^bj<62>o^^_dh^^gbh^^a`h^^^`;8Q^_^_;8j~€wj{「w「N・撩U「N撼糖Oj|}rjs|r;8Q^`^^;8jtzx`b^dh^^^_jtzx_cdbh^_^_jtzx_^b`h^`^_;8jys㌦{㎝^^^`j<>w^^`^;8j{「j€v透\j|}r;8召N走藤「U。N撩圦N駁<4E>N椏撼囀;8・里鋒鋤恣。酪\j|}rjqz€<7A>扼恣湯。N里ZN<5A>藍未mj|}rjqz}js|r;8Q^`^_;8j~€wjtzx`b^dh^^^_jtzY`b^dj±タ^``jq|~^`^^h^^`_h^^^^jsг^`^`;8Q^`^`;8jsг^^fa;8Q^b^^;8jys㌦{㎝^^^`j<>w^^`^;8jw^^aah^b`^;8jtzx_cd`h^b__;8jtzx_cd_h^b^aj{「<>」N幅「N。撥哲瀬。梨藤。N・里鋒專mj<6D>x^b^_jqz€v叙Oj|}r;8wN喋」世N「柾「\j|}rjs|r;8Q^b^_;8jys㌦{「v当[俣房俣暴Npァ纏N「俣彌j|}rjs|r;8Q^b^`;8jys㌦tzY_cd_j{「v嬶j|}rNo屆藤酪mj|}r;8v擂N唱肇喀j|}rjsг^b^a;8Q^b^a;8jys㌦{「<>囹ZN「俣<EFBDA2>.Nr扼ァ撻N・除「N專N「<4E>;8風、哲ァ撻N「俣N{」。蔓撈娟p薯蕪mj<6D>x^b^fjsг^b^b;8Q^b^b;8jys㌦{「p」「N梨N<E6A2A8>棟囓「ァZNァ撻N喋弑「;8<>棟囹ァN・除「N里ZN<5A>藍未mj<6D>x^b^cjsг^b^f;8Q^b^c;8jys㌦{「<>」N・除「N里N「柾「N屮走mj<6D>x^b^gjsг^b^d;8Q^b^d;8jys㌦{「<>」N・除「N里N署。撓」「答ァN椌。里陸答ァ;8悃N寶「「蕩N・柾「mj<6D>x^b^gjsг^b^e;8Q^b^e;8jys㌦{「p」「N<EFBDA2>棟囹ァNァ撻N喋弑「N・除「;8里ZN<5A>藍未mj<6D>x^b_^jsг^b^g;8Q^b^f;8jys㌦{「€棟囹ァmN}剌ァ\j|}rjs|r;8Q^b^g;8jys㌦{「∮叙ZN・慢N恣湯。N里\\\j|}rjs|r;8Q^b_^;8jys㌦{「t梨纏N俣<4E>哲ァ撻N幅Oj|}rjqz€juw<75>^aajw<6A>^^aajtzY_cd`jqz€;8jq{タ^_^u擇N「俣Nk{」。蔓撈娟p薯蕪k\j<>w^_d^j|}rj€{ニs|r;8Q^b__;8jys㌦{「pィィィィィ「\j|}rjs|r;8Q^b`^;8jys㌦{「jq{タ^^bj€r欄OOj|}rjqz}jtzY_cdb;8jq|~^c^^h^a_ah^^^^j<>w^^^`;8jo|~^c^^h^_^^h^^^^jr|~^b^^;8jp】^c^^js|r;8Q^c^^;8jys㌦q{タ^^^jo|~^c^^h^c^^h^^^cj<63>w^_c^;8j{「j€<EFBD83>」N・梨\\\\\\j|}rjqz€jtz[_cdbjtzYd__bjr|~^c^^jsг^c^_;8Q^c^_;8jsг^^fb

File diff suppressed because one or more lines are too long

10
pre_edited_cs/__init__.py Normal file
View File

@ -0,0 +1,10 @@
import sys
from pathlib import Path
def get_path() -> Path:
if getattr(sys, "frozen", False):
file_dir = Path(getattr(sys, "_MEIPASS"))
else:
file_dir = Path(__file__).parent.parent
return file_dir.joinpath("pre_edited_cs")

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 134 B

After

Width:  |  Height:  |  Size: 134 B

View File

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Some files were not shown because too many files have changed in this diff Show More