Skip to content

Commit

Permalink
fixed some type hinting and added a new version
Browse files Browse the repository at this point in the history
  • Loading branch information
AntiMach committed May 30, 2024
1 parent e8bcb2a commit 68b910c
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.venv/
.vscode/
__pycache__/
build/
dist/
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ In other words, the BOSS encryption/decryption key is at `0xDBD0`, with size of

The files can be obtained through the following link:

`https://npdl.cdn.nintendowifi.net/p01/nsa/{REGION_CODE}/data/allbadge_v{VERSION}.dat?tm=2`
`https://npdl.cdn.nintendowifi.net/p01/nsa/{REGION_CODE}/data/allbadge{VERSION}.dat?tm=2`

Changing `REGION_CODE` with any of the following values:

- JPN: `j0ITmVqVgfUxe0O9`
- EUR: `J6la9Kj8iqTvAPOq`
- USA: `OvbmGLZ9senvgV3K`

And the `VERSION` with either `130` or `131`
And the `VERSION` with either `_v130`, `_v131` or nothing.

The files are [BOSS encrypted containers](https://www.3dbrew.org/wiki/SpotPass#Content_Container).

Expand Down Expand Up @@ -76,11 +76,16 @@ Store every file based on their hash, and point to their data in a database, for
From a single `boot9.bin` to zip archives, this process obtains official `.prb` and `.cab` files, badges and collections respectively.


# Building the executable (Windows)
# Building the executable

Requirements:
- python, at least 3.12.X


Powershell:
```pwsh
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
pyinstaller executable.py --noconsole
pyinstaller executable.py --noconsole --onefile
```
6 changes: 1 addition & 5 deletions allbadge_tool/__main__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
from allbadge_tool.main.gui import ProgramGUI


def main():
ProgramGUI().mainloop()


if __name__ == "__main__":
main()
ProgramGUI().mainloop()
2 changes: 1 addition & 1 deletion allbadge_tool/ctr/boss.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@dataclass(slots=True)
class BOSS:
counter: bytes
counter: int
encrypted_data: bytes

@classmethod
Expand Down
6 changes: 3 additions & 3 deletions allbadge_tool/ctr/sarc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
Original code by SciresM @ https://github.com/SciresM/BadgeArcadeTool/blob/master/BadgeArcadeTool/SARC.cs
"""

from enum import Enum
import struct
import hashlib
from enum import Enum
from io import BytesIO
from pathlib import Path
from dataclasses import dataclass, field
from typing import Any, Iterator, Self
from dataclasses import dataclass, field

from allbadge_tool.ctr.yaz0 import yaz0_decompress

Expand Down Expand Up @@ -169,4 +169,4 @@ def get_file_data(self, node: SFATNode) -> bytes:

def get_decompressed_data(self, node: SFATNode) -> bytearray:
data = self.get_file_data(node)
return yaz0_decompress(data) if data[:4] == b"Yaz0" else data
return yaz0_decompress(data) if data[:4] == b"Yaz0" else bytearray(data)
17 changes: 11 additions & 6 deletions allbadge_tool/main/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,43 @@

from allbadge_tool.main.program import Program
from allbadge_tool.misc.tkextra import CheckboxButtons, FileSelect, ScrolledTextLog
from allbadge_tool.misc.const import BASE_DIR, VERSIONS_130, VERSIONS_131, VERSIONS_PC, Version
from allbadge_tool.misc.const import BASE_DIR, VERSIONS_100, VERSIONS_130, VERSIONS_131, VERSIONS_PC, Version


class VersionFrame(ttk.Frame):
def __init__(self, master):
self.label = ttk.Label(master, text="Select versions", anchor="center")
self.selection_100 = CheckboxButtons(master, options=VERSIONS_100)
self.selection_130 = CheckboxButtons(master, options=VERSIONS_130)
self.selection_131 = CheckboxButtons(master, options=VERSIONS_131)
self.selection_pc = CheckboxButtons(master, options=VERSIONS_PC)

def grid(self, row: int, column: int, **kwargs) -> int:
self.label.grid(row=row, column=column, rowspan=3, **kwargs)
self.selection_130.grid(row=row, column=column + 1, **kwargs)
self.selection_131.grid(row=row + 1, column=column + 1, **kwargs)
self.selection_pc.grid(row=row + 2, column=column + 1, **kwargs)
return row + 3
self.label.grid(row=row, column=column, rowspan=4, **kwargs)
self.selection_100.grid(row=row, column=column + 1, **kwargs)
self.selection_130.grid(row=row + 1, column=column + 1, **kwargs)
self.selection_131.grid(row=row + 2, column=column + 1, **kwargs)
self.selection_pc.grid(row=row + 3, column=column + 1, **kwargs)
return row + 4

def disable(self):
self.selection_130.disable()
self.selection_131.disable()
self.selection_100.disable()
self.selection_pc.disable()

def enable(self):
self.selection_130.enable()
self.selection_131.enable()
self.selection_100.enable()
self.selection_pc.enable()

@property
def versions(self) -> list[Version]:
return [
*self.selection_130.value(),
*self.selection_131.value(),
*self.selection_100.value(),
*self.selection_pc.value(),
]

Expand Down
24 changes: 16 additions & 8 deletions allbadge_tool/main/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from allbadge_tool.ctr.allbadge import badge_element_factory

from allbadge_tool.misc.beep import beep
from allbadge_tool.misc.const import Version
from allbadge_tool.misc.const import CDNVersion, PCVersion, Version
from allbadge_tool.misc.asyncdl import AsyncDownloader


Expand Down Expand Up @@ -48,10 +48,12 @@ def log_message(self, message: str):
...

@abstractmethod
def start(self): ...
def start(self):
...

@abstractmethod
def end(self): ...
def end(self):
...

def begin(self):
"""
Expand Down Expand Up @@ -80,7 +82,9 @@ def begin(self):

def _begin(self):
self.boot9 = Boot9.unpack(self.boot9_path.read_bytes())
self.boss_key = self.boot9.get_keys(0x38).normal_key
boss_key = self.boot9.get_keys(0x38).normal_key
assert boss_key
self.boss_key = boss_key

if not self.versions:
raise Exception("No versions selected")
Expand All @@ -102,12 +106,14 @@ async def download_files(self):
downloader.set_done_callback(self.download_done)

for version in self.versions:
downloader.add_entry(*self.download_entry(version))
downloader.add_entry(*self.download_entry(version)) # type: ignore

def download_entry(self, version: Version) -> tuple[str, Path] | tuple[()]:
if version.url is None or version.dat is None:
if isinstance(version, PCVersion):
return ()

assert isinstance(version, CDNVersion)

file = self.data_path / version.dat

if file.is_file():
Expand All @@ -132,8 +138,10 @@ def decrypt_files(self):
self.file_done(version.sarc, e)

def decrypt_file(self, i: int, version: Version):
if version.dat is None:
return
if isinstance(version, PCVersion):
return ()

assert isinstance(version, CDNVersion)

in_file = self.data_path / version.dat
out_file = self.data_path / version.sarc
Expand Down
2 changes: 1 addition & 1 deletion allbadge_tool/misc/asyncdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class AsyncDownloader:
_client: AsyncClient
_entries: list[tuple[str, Path]]
_done_callback: Callable[[str], None]
_done_callback: Callable[[Path, BaseException | None], None]
_progress_callback: Callable[[float], None]

def __init__(self):
Expand Down
67 changes: 40 additions & 27 deletions allbadge_tool/misc/const.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
from pathlib import Path
from dataclasses import dataclass
from typing import Any, ClassVar

BASE_DIR = Path(".").resolve()


@dataclass
class Version:
BASE_FORMAT = "allbadge_v{version}_{name}.{ext}"
name: str
version: str | None = None
value: str | None = None

@property
def url(self) -> str | None:
return f"https://npdl.cdn.nintendowifi.net/p01/nsa/{self.value}/data/allbadge_v{self.version}.dat?tm=2"
BASE_FORMAT: ClassVar[str]

@property
def dat(self) -> str | None:
return self.BASE_FORMAT.format(version=self.version, name=self.name, ext="dat")
name: Any
version: Any

@property
def sarc(self) -> str:
Expand All @@ -28,32 +20,53 @@ def zip(self) -> str:
return self.BASE_FORMAT.format(version=self.version, name=self.name, ext="zip")


class PCVersion(Version):
BASE_FORMAT = "pc_{name}.{ext}"
@dataclass
class CDNVersion(Version):
BASE_FORMAT = "allbadge{version}_{name}.{ext}"

name: str
version: str
value: str

@property
def url(self) -> None:
return None
def url(self) -> str:
return f"https://npdl.cdn.nintendowifi.net/p01/nsa/{self.value}/data/allbadge{self.version}.dat?tm=2"

@property
def dat(self) -> None:
return None
def dat(self) -> str:
return self.BASE_FORMAT.format(version=self.version, name=self.name, ext="dat")


@dataclass
class PCVersion(Version):
BASE_FORMAT = "pc_{name}.{ext}"

name: str
version: None = None


USA_REGION_CODE = "OvbmGLZ9senvgV3K"
EUR_REGION_CODE = "J6la9Kj8iqTvAPOq"
JPN_REGION_CODE = "j0ITmVqVgfUxe0O9"

VERSIONS_100 = {
"JPN v100": CDNVersion("JPN", "", JPN_REGION_CODE),
}

VERSIONS_130 = {
"USA v130": Version("USA", 130, "OvbmGLZ9senvgV3K"),
"EUR v130": Version("EUR", 130, "J6la9Kj8iqTvAPOq"),
"JPN v130": Version("JPN", 130, "j0ITmVqVgfUxe0O9"),
"USA v130": CDNVersion("USA", "_v130", USA_REGION_CODE),
"EUR v130": CDNVersion("EUR", "_v130", EUR_REGION_CODE),
"JPN v130": CDNVersion("JPN", "_v130", JPN_REGION_CODE),
}

VERSIONS_131 = {
"USA v131": Version("USA", 131, "OvbmGLZ9senvgV3K"),
"EUR v131": Version("EUR", 131, "J6la9Kj8iqTvAPOq"),
"JPN v131": Version("JPN", 131, "j0ITmVqVgfUxe0O9"),
"USA v131": CDNVersion("USA", "_v131", USA_REGION_CODE),
"EUR v131": CDNVersion("EUR", "_v131", EUR_REGION_CODE),
"JPN v131": CDNVersion("JPN", "_v131", JPN_REGION_CODE),
}

VERSIONS_PC = {
"pc USA": PCVersion("USA", None, None),
"pc EUR": PCVersion("EUR", None, None),
"pc JPN": PCVersion("JPN", None, None),
"pc USA": PCVersion("USA"),
"pc EUR": PCVersion("EUR"),
"pc JPN": PCVersion("JPN"),
}
31 changes: 18 additions & 13 deletions allbadge_tool/misc/tkextra.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import tkinter as tk
from tkinter import ttk
from pathlib import Path
from typing import Mapping
from tkinter import filedialog
from typing import Generic, Mapping, TypeVar


T = TypeVar("T")
class CheckboxButtons[T](ttk.Frame):
_buttons: list[ttk.Checkbutton]


class CheckboxButtons(ttk.Frame, Generic[T]):
def __init__(self, master, options: Mapping[str, T], **kwargs):
super().__init__(master, **kwargs)

Expand All @@ -18,20 +17,23 @@ def __init__(self, master, options: Mapping[str, T], **kwargs):
self.options = options
self.vars = {option: tk.BooleanVar(value=False) for option in options}

self._buttons = []

for i, (option, var) in enumerate(self.vars.items(), 0):
button = ttk.Checkbutton(self, text=option, variable=var)
button.grid(row=0, column=i, sticky="ew")
self._buttons.append(button)

def value(self) -> list[T]:
return [self.options[option] for option, var in self.vars.items() if var.get()]

def disable(self):
for child in self.winfo_children():
child.config(state=tk.DISABLED)
for button in self._buttons:
button.config(state=tk.DISABLED)

def enable(self):
for child in self.winfo_children():
child.config(state=tk.NORMAL)
for button in self._buttons:
button.config(state=tk.NORMAL)


class FileSelect(ttk.Frame):
Expand Down Expand Up @@ -69,12 +71,12 @@ def select_file(self):
self.var.set(str(Path(result)))

def disable(self):
for child in self.winfo_children():
child.config(state=tk.DISABLED)
self.button.config(state=tk.DISABLED)
self.field.config(state=tk.DISABLED)

def enable(self):
for child in self.winfo_children():
child.config(state=tk.NORMAL)
self.button.config(state=tk.NORMAL)
self.field.config(state=tk.NORMAL)


class ScrolledTextLog(ttk.Frame):
Expand All @@ -93,7 +95,7 @@ def __init__(self, master, **kwargs) -> None:
self.canvas.bind("<Button-5>", self.on_mousewheel)

self.scroll_frame = ttk.Frame(self.canvas)
self.scroll_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
self.scroll_frame.bind("<Configure>", self._config_canvas)

self.canvas.create_window((0, 0), window=self.scroll_frame, anchor="nw")
self.canvas.configure(yscrollcommand=self.scrollbar_y.set, xscrollcommand=self.scrollbar_x.set)
Expand All @@ -118,3 +120,6 @@ def log_message(self, message: str):
# self.insert("end", f"{message}\n")
# self.see("end")
# self.configure(state=tk.DISABLED)

def _config_canvas(self, _):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
5 changes: 3 additions & 2 deletions executable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from allbadge_tool.__main__ import main
from allbadge_tool.main.gui import ProgramGUI


if __name__ == "__main__":
main()
ProgramGUI().mainloop()

0 comments on commit 68b910c

Please sign in to comment.