Skip to content

Commit

Permalink
cleanup nwnnsscomp abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
th3w1zard1 committed Mar 25, 2024
1 parent 9a5fb1f commit bf4c399
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self, message: str):


class EntryPointError(CompileError): ...
class MissingIncludeError(CompileError): ...


class TopLevelObject(ABC):
Expand Down
29 changes: 9 additions & 20 deletions Libraries/PyKotor/src/pykotor/resource/formats/ncs/compilers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
if TYPE_CHECKING:
import os

from subprocess import CompletedProcess

from pykotor.resource.formats.ncs.ncs_data import NCS, NCSOptimizer


Expand All @@ -33,22 +35,16 @@ def compile_script( # noqa: PLR0913
):
source_filepath: Path = Path.pathify(source_path)
nss_data: bytes = BinaryReader.load_file(source_filepath)
nss_contents: str = decode_bytes_with_fallbacks(nss_data) # .replace('#include "k_inc_debug"', "")
nss_contents: str = decode_bytes_with_fallbacks(nss_data)
ncs: NCS = compile_nss(nss_contents, game, optimizers, library_lookup=[source_filepath.parent], debug=debug)
write_ncs(ncs, output_path)


class ExternalCompilerFeatures(NamedTuple):
can_compile: bool
can_decompile: bool


class ExternalCompilerConfig(NamedTuple):
sha256: str
name: str
release_date: date
author: str
features: ExternalCompilerFeatures
commandline: dict[str, list[str]]


Expand All @@ -58,7 +54,6 @@ class KnownExternalCompilers(Enum):
name="TSLPatcher",
release_date=date(2009, 1, 1),
author="todo",
features=ExternalCompilerFeatures(can_compile=True, can_decompile=True),
commandline={
"compile": ["-c", "{source}", "-o", "{output}"],
"decompile": ["-d", "{source}", "-o", "{output}"],
Expand All @@ -69,7 +64,6 @@ class KnownExternalCompilers(Enum):
name="KOTOR Tool",
release_date=date(2005, 1, 1),
author="Fred Tetra",
features=ExternalCompilerFeatures(can_compile=True, can_decompile=True),
commandline={
"compile": ["-c", "--outputdir", "{output_dir}", "-o", "{output_name}", "-g", "{game_value}", "{source}"],
"decompile": ["-d", "--outputdir", "{output_dir}", "-o", "{output_name}", "-g", "{game_value}", "{source}"],
Expand All @@ -80,7 +74,6 @@ class KnownExternalCompilers(Enum):
name="v1.3 first public release",
release_date=date(2003, 12, 31),
author="todo",
features=ExternalCompilerFeatures(can_compile=True, can_decompile=True),
commandline={
"compile": ["-c", "{source}", "{output}"],
"decompile": ["-d", "{source}", "{output}"],
Expand All @@ -91,7 +84,6 @@ class KnownExternalCompilers(Enum):
name="KOTOR Scripting Tool",
release_date=date(2016, 5, 18),
author="James Goad", # TODO: double check
features=ExternalCompilerFeatures(can_compile=True, can_decompile=True),
commandline={
"compile": ["-c", "--outputdir", "{output_dir}", "-o", "{output_name}", "-g", "{game_value}", "{source}"],
"decompile": ["-d", "--outputdir", "{output_dir}", "-o", "{output_name}", "-g", "{game_value}", "{source}"],
Expand All @@ -102,23 +94,20 @@ class KnownExternalCompilers(Enum):
name="DeNCS",
release_date=date(2006, 5, 30),
author="todo",
features=ExternalCompilerFeatures(can_compile=True, can_decompile=True),
commandline={},
)
XOREOS = ExternalCompilerConfig(
sha256="todo",
name="Xoreos Tools",
release_date=date(1, 1, 1),
author="todo",
features=ExternalCompilerFeatures(can_compile=False, can_decompile=True),
commandline={},
)
KNSSCOMP = ExternalCompilerConfig( # TODO: add hash and look for this in tslpatcher.reader.ConfigReader.load_compile_list()
sha256="todo",
name="knsscomp",
release_date=date(1, 1, 1), # 2022?
author="Nick Hugi",
features=ExternalCompilerFeatures(can_compile=True, can_decompile=False),
commandline={},
)

Expand Down Expand Up @@ -179,8 +168,8 @@ def __init__(self, nwnnsscomp_path: os.PathLike | str):
self.filehash: str
self.change_nwnnsscomp_path(nwnnsscomp_path)

def get_info(self) -> ExternalCompilerConfig:
return KnownExternalCompilers.from_sha256(self.filehash).value
def get_info(self) -> KnownExternalCompilers:
return KnownExternalCompilers.from_sha256(self.filehash)

def change_nwnnsscomp_path(self, nwnnsscomp_path: os.PathLike | str):
self.nwnnsscomp_path: Path = Path.pathify(nwnnsscomp_path)
Expand Down Expand Up @@ -255,7 +244,7 @@ def compile_script(
"""
config: NwnnsscompConfig = self.config(source_file, output_file, game)

result: subprocess.CompletedProcess[str] = subprocess.run(
result: CompletedProcess[str] = subprocess.run(
args=config.get_compile_args(str(self.nwnnsscomp_path)),
capture_output=True, # Capture stdout and stderr
text=True,
Expand Down Expand Up @@ -294,7 +283,7 @@ def decompile_script(
"""
config: NwnnsscompConfig = self.config(source_file, output_file, game)

result: subprocess.CompletedProcess[str] = subprocess.run(
result: CompletedProcess[str] = subprocess.run(
args=config.get_decompile_args(str(self.nwnnsscomp_path)),
capture_output=True, # Capture stdout and stderr
text=True,
Expand All @@ -304,10 +293,10 @@ def decompile_script(

return self._get_output(result)

def _get_output(self, result: subprocess.CompletedProcess[str]) -> tuple[str, str]:
def _get_output(self, result: CompletedProcess[str]) -> tuple[str, str]:
stdout: str = result.stdout
stderr: str = (
f"no error provided but return code is nonzero: ({result.returncode})"
f"No error provided, but return code is nonzero: ({result.returncode})"
if result.returncode != 0 and (not result.stderr or not result.stderr.strip())
else result.stderr
)
Expand Down
67 changes: 65 additions & 2 deletions Libraries/PyKotor/src/pykotor/tools/registry.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
from __future__ import annotations

from contextlib import suppress
import os

from contextlib import suppress
from typing import TYPE_CHECKING

from pykotor.common.misc import Game
from utility.error_handling import format_exception_with_variables
from utility.misc import ProcessorArchitecture
from utility.system.path import Path

if TYPE_CHECKING:
import types

from typing_extensions import Self

KOTOR_REG_PATHS = {
Game.K1: {
Expand Down Expand Up @@ -215,7 +223,62 @@ def create_registry_path(hive, path): # sourcery skip: raise-from-previous-erro
print(format_exception_with_variables(e))


def set_registry_key_value(full_key_path, value_name, value_data):
def get_retail_key(game: Game):
if ProcessorArchitecture.from_os() == ProcessorArchitecture.BIT_64:
return (
r"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\LucasArts\KotOR2"
if game.is_k2()
else r"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\BioWare\SW\KOTOR"
)
return (
r"HKEY_LOCAL_MACHINE\SOFTWARE\LucasArts\KotOR2"
if game.is_k2()
else r"HKEY_LOCAL_MACHINE\SOFTWARE\BioWare\SW\KOTOR"
)

class SpoofKotorRegistry:
"""A context manager used to safely spoof a registry path temporarily."""
def __init__(
self,
installation_path: os.PathLike | str,
game: Game | None = None,
):
from pykotor.extract.installation import Installation

# Key name at the path containing the value.
self.key: str = "Path"
self.spoofed_path: Path = Path.pathify(installation_path).resolve()

if game is not None:
determined_game = game
else:
determined_game = Installation.determine_game(installation_path)
if determined_game is None:
raise ValueError(f"Could not auto-determine the game k1 or k2 from '{installation_path}'. Try sending 'game' enum to prevent auto-detections like this.")

# Path to the key.
self.registry_path: str = get_retail_key(determined_game)

# The original contents of the key. None if not existing.
self.original_value: str | None = resolve_reg_key_to_path(self.registry_path, self.key)

def __enter__(self) -> Self:
if self.spoofed_path == self.original_value:
set_registry_key_value(self.registry_path, self.key, str(self.spoofed_path))
return self

def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: types.TracebackType | None,
):
# Revert the registry key to its original value if it was altered
if self.original_value is not None:
set_registry_key_value(self.registry_path, self.key, self.original_value)
# TODO(th3w1zard1): Determine what to do if the regpath never existed, as deleting it isn't easy. Set it to ""?

def set_registry_key_value(full_key_path: str, value_name: str, value_data: str):
"""Sets a registry key value, creating the key (and its parents, if necessary).
Args:
Expand Down
2 changes: 2 additions & 0 deletions Libraries/Utility/src/utility/system/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ def __repr__(self):
return f"{self.__class__.__name__}({self!s})"

def __eq__(self, __value):
if __value is None:
return False
if self is __value:
return True
if isinstance(__value, (bytes, bytearray, memoryview)):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os

from typing import Any

from PyQt5 import QtCore
Expand Down
Loading

0 comments on commit bf4c399

Please sign in to comment.