Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions Cachyos/Scripts/WIP/emu/cia_3ds_decryptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,22 @@ def build_ncch_args_sequential(bin_dir: Path) -> str:
)


def build_ncch_args_contentid(bin_dir: Path, content_txt: Path) -> str:
def _extract_content_ids(content_txt: Path) -> list[int]:
if not content_txt.exists():
return []

content_ids = []
if content_txt.exists():
for line in content_txt.read_text(errors="replace").splitlines():
if "ContentId:" in line:
cid = line.split("ContentId:")[1].strip()[:8]
if cid:
content_ids.append(int(cid, 16))
for line in content_txt.read_text(errors="replace").splitlines():
if "ContentId:" not in line:
continue
cid = line.split("ContentId:")[1].strip()[:8]
if cid:
content_ids.append(int(cid, 16))
Comment on lines +229 to +230
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The call to int(cid, 16) is not wrapped in a try...except block. If content.txt contains a malformed ContentID (e.g., ContentId: not-a-hex-value), this will raise a ValueError and crash the script. It's safer to handle this potential error to make the parsing more robust.

      if cid:
        try:
          content_ids.append(int(cid, 16))
        except ValueError:
          logging.warning("Ignoring invalid ContentID format in %s: '%s'", content_txt.name, cid)

return content_ids


def build_ncch_args_contentid(bin_dir: Path, content_txt: Path) -> str:
content_ids = _extract_content_ids(content_txt)
ncch_files = sorted(bin_dir.glob("tmp.*.ncch"))
parts = [
f'-i "{ncch}:{i}:{content_ids[i] if i < len(content_ids) else i}"'
Expand Down
67 changes: 67 additions & 0 deletions Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
import importlib.util
import unittest
import sys
from pathlib import Path
import tempfile

# Dynamically import cia_3ds_decryptor.py
file_path = Path(__file__).parent / "cia_3ds_decryptor.py"
spec = importlib.util.spec_from_file_location("cia_3ds_decryptor", str(file_path))
if spec is None:
raise ImportError(f"Could not load {file_path}")
decryptor = importlib.util.module_from_spec(spec)
sys.modules["cia_3ds_decryptor"] = decryptor
spec.loader.exec_module(decryptor)

Check warning on line 15 in Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py#L15

Item "None" of "Loader | None" has no attribute "exec_module". (union-attr)

class TestDecryptorContentId(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.TemporaryDirectory()
self.bin_dir = Path(self.temp_dir.name)
self.content_txt = self.bin_dir / "content.txt"

def tearDown(self):
self.temp_dir.cleanup()

def test_extract_content_ids_no_file(self):

Check notice on line 26 in Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py#L26

Method name "test_extract_content_ids_no_file" doesn't conform to '[a-z_][a-z0-9_]{2,30}$' pattern
self.assertEqual(decryptor._extract_content_ids(self.content_txt), [])

def test_extract_content_ids_valid_file(self):

Check notice on line 29 in Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py#L29

Method name "test_extract_content_ids_valid_file" doesn't conform to '[a-z_][a-z0-9_]{2,30}$' pattern
content = (
"ContentId: 00000001\n"
"Some other line\n"
"ContentId: 00000002 \n"
"ContentId:\n" # Invalid, empty after split
)
Comment on lines +30 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This is a good set of test cases. To make it even more robust, I'd suggest adding a case with a malformed hex value for the ContentID. This would ensure the parser can gracefully handle such errors without crashing, which it currently does not. This test will fail until the corresponding error handling is added to _extract_content_ids.

Suggested change
content = (
"ContentId: 00000001\n"
"Some other line\n"
"ContentId: 00000002 \n"
"ContentId:\n" # Invalid, empty after split
)
content = (
"ContentId: 00000001\n"
"Some other line\n"
"ContentId: 00000002 \n"
"ContentId: not-a-hex\n" # Should be gracefully ignored
"ContentId:\n" # Invalid, empty after split
)

self.content_txt.write_text(content)
expected = [1, 2]
self.assertEqual(decryptor._extract_content_ids(self.content_txt), expected)

def test_build_ncch_args_contentid(self):
content = (
"ContentId: 0000000A\n"
"ContentId: 0000000B\n"
)
self.content_txt.write_text(content)

# Create some fake ncch files
(self.bin_dir / "tmp.0.ncch").touch()
(self.bin_dir / "tmp.1.ncch").touch()
(self.bin_dir / "tmp.2.ncch").touch() # More files than IDs

ncch0 = self.bin_dir / "tmp.0.ncch"
ncch1 = self.bin_dir / "tmp.1.ncch"
ncch2 = self.bin_dir / "tmp.2.ncch"

args = decryptor.build_ncch_args_contentid(self.bin_dir, self.content_txt)

# Expect content ids 10 and 11, and fallback to 2 for the last one
expected_parts = [
f'-i "{ncch0}:0:10"',
f'-i "{ncch1}:1:11"',
f'-i "{ncch2}:2:2"',
]
self.assertEqual(args, " ".join(expected_parts))

if __name__ == '__main__':

Check notice on line 66 in Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Cachyos/Scripts/WIP/emu/test_decryptor_contentid.py#L66

expected 2 blank lines after class or function definition, found 1 (E305)
unittest.main()
Loading