Skip to content

Commit

Permalink
v0.2.1
Browse files Browse the repository at this point in the history
Made sense out of the patching versioning, tidy things up
  • Loading branch information
antipatico committed Apr 18, 2024
1 parent 3cc7c81 commit 5674b49
Show file tree
Hide file tree
Showing 19 changed files with 243 additions and 246 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@ A refactor is needed to make the version system make sense.
You can list the available patches using the `list-patches` command:
```
$ balatromobile list-patches
Name Description Platforms
------------------------- ----------------------------------------------------------------------------------------------- -----------
basic Basic set of patches needed to make the game run on mobile android
landscape Forces the game to always stay in landscape mode, ignoring the screeen orentation of the device android,ios
landscape-hidpi Forces the game to always stay in landscape mode and apply hidpi fix for iOS ios
crt Disable CRT effect [Fixes blackscreen bug on Pixels and other devices] android,ios
fps Cap the FPS limit to the FPS limit of the screen android,ios
external-storage Save game files under /sdcard/Android [Works well for Android < 13] android
portmaster-simple-fx Disable gameplay visible behind menu background, shadows, and bloom effects. From PortMaster android,ios
portmaster-no-background Disable background animations and effects. From PortMaster android,ios
portmaster-square-display Optimize for square and square-like displays. From PortMaster android,ios
portmaster-nunito-font Replace the main font used with nunito, optimized for smaller displays. From PortMaster android,ios
Name Description Platforms
---------------- ----------------------------------------------------------------------------------------------- -----------
basic Basic set of patches needed to make the game run on mobile android
crt Disable CRT effect [Fixes blackscreen bug on Pixels and other devices] android,ios
external-storage Save game files under /sdcard/Android [Works well for Android < 13] android
fps Cap the FPS limit to the FPS limit of the screen android,ios
landscape-hidpi Forces the game to always stay in landscape mode and apply hidpi fix for iOS ios
landscape Forces the game to always stay in landscape mode, ignoring the screeen orentation of the device android,ios
no-background Disable background animations and effects. From PortMaster android,ios
nunito-font Replace the main font used with nunito, optimized for smaller displays. From PortMaster android,ios
simple-fx Disable gameplay visible behind menu background, shadows, and bloom effects. From PortMaster android,ios
square-display Optimize for square and square-like displays. From PortMaster android,ios
```
It is possible to specify the list of patches you want to apply by supplying a comma-separated list of patches, for example:
```bash
Expand Down
2 changes: 1 addition & 1 deletion balatromobile/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__="0.2.0"
__version__="0.2.1"
6 changes: 3 additions & 3 deletions balatromobile/gamble.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from .resources import all_artifacts
from .patcher import all_patches, select_patches, DEFAULT_PATCHES
from .utils import is_java_installed, run_silent
from .utils import get_balatro_version, is_java_installed, run_silent
from .__version__ import __version__


Expand Down Expand Up @@ -38,9 +38,9 @@ def android(args: Namespace):
balatro = Path(d) / "Balatro"
with ZipFile(balatro_exe, "r") as z:
z.extractall(balatro)
balatro_version = get_balatro_version(balatro)
for patch in patches:
patch.apply(balatro)
balatro_version = (balatro / "version.jkr").read_text().splitlines()[0]
patch.apply(balatro, balatro_version)
app = Path(d) / "balatro_app"
run_silent(["java", "-jar", artifacts.apk_editor.absolute(), "d", "-i", artifacts.love_apk.absolute(), "-o", app.absolute()])
manifest_tpl = artifacts.android_manifest.read_text()
Expand Down
103 changes: 52 additions & 51 deletions balatromobile/patcher.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,62 @@
import tomllib
from pathlib import Path
from hashlib import sha256
from .resources import get_patch, get_artifact

DEFAULT_PATCHES = "basic,landscape,crt,fps"


class Patch:
def __init__(self, patch: dict):
self.target_file = patch["target_file"]
self.hashes = patch["supported_hashes"]
self.search_string = patch.get("search_string", None)
self.content = patch.get("patch_content", None)
self.content = patch.get("patch_content", "")
artifact_name = patch.get("artifact", None)
self.artifact = get_artifact(artifact_name) if artifact_name is not None else None

def check_checksum(self, balatro: Path):
target = balatro / Path(self.target_file)
if not target.exists() or not target.is_file():
return False
if "skip" in self.hashes and len(self.hashes) == 1:
return True
hashsum = sha256(target.read_bytes()).hexdigest()
return f"sha256:{hashsum[:10]}" in self.hashes
if self.search_string == self.artifact == None:
raise Exception(f"Empty patches are not allowed")

def apply(self, balatro: Path):
target = balatro / Path(self.target_file)
def apply(self, target_file: Path):
target =target_file
if self.artifact is not None:
target.write_bytes(self.artifact.read_bytes())
return
patched = "\n".join([l if self.search_string not in l else self.content for l in target.read_text().splitlines()])
target.write_text(patched)


class PatchFile:
def __init__(self, patch_file: dict):
self.target_file = Path(patch_file["target_file"])
self.patches = [Patch(p) for p in patch_file["patches"]]

def apply_all(self, balatro: Path):
[p.apply(balatro / self.target_file) for p in self.patches]


class PatchList:
def __init__(self, patch_list: list):
self.patches = [Patch(p) for p in patch_list]
def __init__(self, patch_list: dict):
# Must be unique across patch lists to differentiate them
self.version = patch_list['version']
# If not defined, skip version checking
self.supported_game_versions = patch_list.get('supported_game_versions', None)
self.patch_files = [PatchFile(p) for p in patch_list["patch_files"]]

def is_compatible(self, balatro: Path):
return True # Disabling it for now, it may be sensible to save the game version instead. A problem is patching multiple times the save file.
#return all(p.check_checksum(balatro) for p in self.patches)
def is_compatible(self, version: str):
return self.supported_game_versions is None or version in self.supported_game_versions

def apply_all(self, balatro: Path):
for p in self.patches:
p.apply(balatro)
[p.apply_all(balatro) for p in self.patch_files]


class PatchFile:
def __init__(self, filename):
self.path = get_patch(filename)
class VersionedPatch:
def __init__(self, name: str):
self.path = get_patch(f"{name}.toml")
with open(self.path,"rb") as f:
toml = tomllib.load(f)
self.name : str = toml["name"]
self.name : str = name
self.description : str = toml["description"]
self.authors : list = toml["authors"]
self.supported_platforms : list = toml["supported_platforms"]
self.versions = {}
for k, v in toml['versions'].items():
self.versions[k] = PatchList(v)
self.patch_lists = [PatchList(p) for p in toml['patch_lists']]

def supports_android(self) -> bool:
return "android" in self.supported_platforms
Expand All @@ -66,37 +65,39 @@ def supports_ios(self) -> bool:
return "ios" in self.supported_platforms

def __str__(self) -> str:
return f'PatchFile(name="{self.name}", description="{self.description}", supported_platforms=[{",".join(self.supported_platforms)}])'
return f'VersionedPatch(name="{self.name}", description="{self.description}", supported_platforms=[{",".join(self.supported_platforms)}])'

def __repr__(self) -> str:
return str(self)

def apply(self, balatro: Path) -> str:
for k, v in self.versions.items():
if v.is_compatible(balatro):
v.apply_all(balatro)
return k
raise Exception(f'Cannot find any compatible patch version with given Balatro.exe for {self}')
def apply(self, balatro: Path, version: str) -> int:
# TODO: allow user to specify specific patch version
# TODO: allow user to force patch
for p in self.patch_lists:
if p.is_compatible(version):
p.apply_all(balatro)
return p.version
raise Exception(f'Cannot find any compatible Patch version of "{self.name}" for given Balatro.exe having game version "{version}"')


def all_patches() -> list[PatchFile]:
def all_patches() -> list[VersionedPatch]:
return [
PatchFile("basic.toml"),
PatchFile("landscape.toml"),
PatchFile("landscape-hidpi.toml"),
PatchFile("crt.toml"),
PatchFile("fps.toml"),
PatchFile("external-storage.toml"),
PatchFile("portmaster-simple-fx.toml"),
PatchFile("portmaster-no-background.toml"),
PatchFile("portmaster-square-display.toml"),
PatchFile("portmaster-nunito-font.toml"),
VersionedPatch("basic"),
VersionedPatch("crt"),
VersionedPatch("external-storage"),
VersionedPatch("fps"),
VersionedPatch("landscape-hidpi"),
VersionedPatch("landscape"),
VersionedPatch("no-background"),
VersionedPatch("nunito-font"),
VersionedPatch("simple-fx"),
VersionedPatch("square-display"),
]

def select_patches(patches: str) -> list[PatchFile]:
def select_patches(patches: str) -> list[VersionedPatch]:
desired_patches = patches.split(",")
patch_files : list[PatchFile] = list(filter(lambda p: p.name in desired_patches, all_patches()))
patch_files : list[VersionedPatch] = list(filter(lambda p: p.name in desired_patches, all_patches()))
if len(desired_patches) != len(patch_files):
missing_patches = [p for p in desired_patches if p not in [P.name for P in patch_files]]
raise ValueError(f'One or more patches not found: {",".join(missing_patches)}')
return patch_files
raise Exception(f'One or more patches not found: {",".join(missing_patches)}')
return patch_files
25 changes: 13 additions & 12 deletions balatromobile/patches/basic.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
name = "basic"
description = "Basic set of patches needed to make the game run on mobile"
authors = ["blake502"]
supported_platforms = ["android"]


[[versions.v0]]
target_file = "globals.lua"
supported_hashes = ["sha256:2cf3296712"]
search_string = "loadstring"
patch_content = """
[[patch_lists]]
version = 0
supported_game_versions = ["1.0.1c-FULL"]
[[patch_lists.patch_files]]
target_file = "globals.lua"
[[patch_lists.patch_files.patches]]
search_string = "loadstring"
patch_content = """
if love.system.getOS() == 'Android' then
self.F_DISCORD = true
self.F_NO_ACHIEVEMENTS = true
Expand All @@ -19,10 +20,10 @@ patch_content = """
end
"""

[[versions.v0]]
target_file = "functions/button_callbacks.lua"
supported_hashes = ["sha256:e96dd625c1"]
search_string = "G.CONTROLLER.text_input_hook == e and G.CONTROLLER.HID.controller"
patch_content = """
[[patch_lists.patch_files]]
target_file = "functions/button_callbacks.lua"
[[patch_lists.patch_files.patches]]
search_string = "G.CONTROLLER.text_input_hook == e and G.CONTROLLER.HID.controller"
patch_content = """
if G.CONTROLLER.text_input_hook == e and (G.CONTROLLER.HID.controller or G.CONTROLLER.HID.touch) then
"""
26 changes: 13 additions & 13 deletions balatromobile/patches/crt.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
name = "crt"
description = "Disable CRT effect [Fixes blackscreen bug on Pixels and other devices]"
authors = ["blake502"]
supported_platforms = ["android", "ios"]


[[versions.v0]]
target_file = "globals.lua"
supported_hashes = ["skip"]
search_string = "crt = "
patch_content = """
[[patch_lists]]
version = 0
# supported_game_versions = ALL
[[patch_lists.patch_files]]
target_file = "globals.lua"
[[patch_lists.patch_files.patches]]
search_string = "crt = "
patch_content = """
crt = 0,
"""

[[versions.v0]]
target_file = "game.lua"
supported_hashes = ["skip"]
search_string = "G.SHADERS['CRT'])"
patch_content = ""
[[patch_lists.patch_files]]
target_file = "game.lua"
[[patch_lists.patch_files.patches]]
search_string = "G.SHADERS['CRT'])"
# patch_content = ""
15 changes: 8 additions & 7 deletions balatromobile/patches/external-storage.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
name = "external-storage"
description = "Save game files under /sdcard/Android [Works well for Android < 13]"
authors = ["blake502"]
supported_platforms = ["android"]


[[versions.v0]]
target_file = "conf.lua"
supported_hashes = ["sha256:1e2499c2cb"]
search_string = "function love.conf(t)"
patch_content = """
[[patch_lists]]
version = 0
# supported_game_versions = ALL
[[patch_lists.patch_files]]
target_file = "conf.lua"
[[patch_lists.patch_files.patches]]
search_string = "function love.conf(t)"
patch_content = """
function love.conf(t)
t.externalstorage = true
"""
15 changes: 8 additions & 7 deletions balatromobile/patches/fps.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
name = "fps"
description = "Cap the FPS limit to the FPS limit of the screen"
authors = ["blake502"]
supported_platforms = ["android", "ios"]


[[versions.v0]]
target_file = "main.lua"
supported_hashes = ["skip"]
search_string = "G.FPS_CAP = G.FPS_CAP or"
patch_content = """
[[patch_lists]]
version = 0
# supported_game_versions = ALL
[[patch_lists.patch_files]]
target_file = "main.lua"
[[patch_lists.patch_files.patches]]
search_string = "G.FPS_CAP = G.FPS_CAP or"
patch_content = """
p_ww, p_hh, p_wflags = love.window.getMode()
G.FPS_CAP = p_wflags['refreshrate']
"""
15 changes: 8 additions & 7 deletions balatromobile/patches/landscape-hidpi.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
name = "landscape-hidpi"
description = "Forces the game to always stay in landscape mode and apply hidpi fix for iOS"
authors = ["blake502"]
supported_platforms = ["ios"]


[[versions.v0]]
target_file = "main.lua"
supported_hashes = ["sha256:f2cb21e757"]
search_string = "local os = love.system.getOS()"
patch_content = """
[[patch_lists]]
version = 0
# supported_game_versions = ALL
[[patch_lists.patch_files]]
target_file = "main.lua"
[[patch_lists.patch_files.patches]]
search_string = "local os = love.system.getOS()"
patch_content = """
local os = love.system.getOS()
love.window.setMode(2, 1, {highdpi = true})
"""
15 changes: 8 additions & 7 deletions balatromobile/patches/landscape.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
name = "landscape"
description = "Forces the game to always stay in landscape mode, ignoring the screeen orentation of the device"
authors = ["blake502"]
supported_platforms = ["android", "ios"]


[[versions.v0]]
target_file = "main.lua"
supported_hashes = ["sha256:f2cb21e757"]
search_string = "local os = love.system.getOS()"
patch_content = """
[[patch_lists]]
version = 0
# supported_game_versions = ALL
[[patch_lists.patch_files]]
target_file = "main.lua"
[[patch_lists.patch_files.patches]]
search_string = "local os = love.system.getOS()"
patch_content = """
local os = love.system.getOS()
love.window.setMode(2, 1)
"""
Loading

0 comments on commit 5674b49

Please sign in to comment.