51 Commits

Author SHA1 Message Date
ihaveahax
d656b1793c fix misplaced frozen variable setting 2026-01-08 18:31:01 -06:00
ihaveahax
50a7117aa9 move bin and title.db.gx to inside custominstall folder 2026-01-08 18:30:04 -06:00
ihaveahax
a0234e9b53 try to search for save3ds_fuse in PATH, if not found locally 2026-01-08 18:04:03 -06:00
ihaveahax
ffcf536d58 gui: search boot9strap folder too for input files (fixes #83) 2026-01-08 17:50:02 -06:00
ihaveahax
17aebb3256 fix corruption issue by moving corrupted files after closing them (fixes #86) 2026-01-08 17:47:18 -06:00
ihaveahax
09dbf134f1 initial python packaging and nix flake 2026-01-08 17:42:35 -06:00
ihaveahax
c61b2bf168 update CONTRIBUTING.md 2026-01-07 22:38:29 -06:00
ihaveahax
c276dc82bc CONTRIBUTING.md 2025-09-30 13:50:01 -05:00
ihaveahax
e5725876e2 Merge pull request #76 from samuelplaca/compilation-flag-fix
Update cflags in Makefile of finalize
2024-04-17 19:10:12 -05:00
Samuel Plaça
2d78e0bc32 update deprecated cflags out of finalize Makefile
3ds.h currently prints a Warning when it detects the usage of compilation
flags -DARM11 -D_3DS, stating that -D__3DS__ should be used in their stead.
See 48967dc417
2024-04-17 13:47:28 -03:00
Ian Burgwin
9ab8236a78 add shebang to custominstall and ci-gui 2022-11-25 00:14:28 -08:00
ihaveahax
1be4221186 Merge pull request #65 from TaiAurori/safe-install
Correct README's file path for compiled save3ds_fuse
2022-05-17 02:06:21 -07:00
TaiAurori
6a770c40c0 README: correct save3ds_fuse compiled binary path 2022-05-16 10:42:01 -04:00
Ian Burgwin
da1a7393b0 README: point to correct branch 2022-04-25 13:25:35 -07:00
Ian Burgwin
6a5ca17a33 custominstall: fix --seeddb by loading it earlier 2022-04-02 11:28:54 -07:00
Ian Burgwin
8f90387a80 bump max allowed pyctr version 2022-04-02 11:24:00 -07:00
Ian Burgwin
83c6d07194 codesign save3ds_fuse mac binary 2022-02-04 17:31:29 -08:00
Ian Burgwin
a1b3cb059e update cargo command for linux 2022-01-30 20:27:30 -08:00
Ian Burgwin
68f6bfbb2e update save3ds_fuse binaries 2022-01-30 20:23:21 -08:00
Ian Burgwin
d12684d8bf version 2.1 2021-09-12 09:02:27 -07:00
Ian Burgwin
54ae8a504c check for id0 (closes #49) 2021-09-12 08:50:59 -07:00
Ian Burgwin
d97e11e4ec use setup script to build cx-freeze standalone 2021-07-26 11:31:34 -07:00
Ian Burgwin
c3448c388e ci-gui: use relative path when loading tcl (to fix non-latin characters in the absolute path), prevent taskbar lib errors from causing an exit 2021-07-13 07:08:36 -07:00
Ian Burgwin
4d7be0812e version 2.1b4 & finalize 1.6 2021-07-08 07:43:13 -07:00
Ian Burgwin
8c60eecec5 finalize: remove useless frees 2021-07-08 07:35:26 -07:00
Ian Burgwin
653569093d finalize: ensure *entries is initialized to NULL 2021-07-08 07:34:07 -07:00
Ian Burgwin
adccac9ee7 requirements-win32: bump comtypes to 1.1.10 2021-07-08 07:12:44 -07:00
Ian Burgwin
8629cbee8e requirements: bump pyctr to >=0.4,<0.6 2021-07-08 07:09:25 -07:00
Ian Burgwin
740844e57a Merge branch 'safe-install' of github.com:ihaveamac/custom-install into safe-install 2021-05-06 08:39:53 -07:00
Ian Burgwin
d847043045 requirements: relax pyctr requirement: >=0.4,<0.5 2021-05-06 08:39:33 -07:00
Ian Burgwin
217a508bf3 Merge pull request #47 from TimmSkiller/safe-install
Added checks for existing Ticket and entry in Title Database
2021-04-14 14:56:40 -07:00
TimmSkiller
42ec2d760a added missing break statement 2021-04-15 00:19:01 +03:00
TimmSkiller
938d8fd6aa Added check for existing ticket and title entry before finalizing 2021-04-15 00:16:15 +03:00
Ian Burgwin
ac0be9d61d custominstall: force utf-8 encoding for the output of save3ds_fuse (fixes #41 hopefully) 2021-03-22 06:55:40 -07:00
Ian Burgwin
d231e9c043 Merge pull request #43 from Jisxu/safe-install
auto input filename include  boot9.bin,seeddb.bin,movable.sed
2021-03-22 01:14:59 -07:00
Justin
9c3c4ce5f9 log filename 2021-03-22 15:59:54 +08:00
Justin
6a324b9388 add click callback function 2021-03-22 15:49:31 +08:00
Justin
647e56cf05 auto input filename include boot9.bin,seeddb.bin,movable.sed when they in <sdcard>:/gm9/out 2021-03-22 14:18:12 +08:00
Ian Burgwin
643e4e4976 custominstall: show 300 title warning (fixes #42) 2021-03-12 18:26:04 -08:00
Ian Burgwin
09ed0093df custominstall: post-release version bump 2021-03-12 18:24:10 -08:00
Ian Burgwin
9b7346c919 version 2.1b3 2021-03-08 18:16:58 -08:00
Ian Burgwin
38f5e2b0e6 custominstall: remove seek workaround for CDN contents (fixed in pyctr 0.4.6) 2021-03-08 18:08:32 -08:00
Ian Burgwin
f48e177604 bump pyctr -> 0.4.6, comtypes -> 1.1.8 2021-03-08 18:08:06 -08:00
Ian Burgwin
4ca2c59b5a custominstall: fix cdn content install 2021-02-24 15:19:48 -08:00
Ian Burgwin
7a68b23365 custominstall: create title.db and import.db if missing 2021-02-24 15:07:59 -08:00
Ian Burgwin
1dec5175ea add title.db.gz, add to standalone build 2021-02-24 14:17:17 -08:00
Ian Burgwin
4d223ed931 show warning if 300 titles are detected 2021-02-13 23:08:37 -08:00
Ian Burgwin
46a0d985a7 vesion 2.1b2 2021-02-12 20:58:13 -08:00
Ian Burgwin
37112682a0 show mid-install status per-title 2021-02-09 21:21:34 -08:00
Ian Burgwin
9c777adf26 ci-gui: ensure boot9 is loaded before allowing titles to be added, show better error message for missing seeddb, load seeddb before starting and any time a new one is selected 2021-02-09 20:24:34 -08:00
Ian Burgwin
b3eae08f27 custominstall: make boot9 and seeddb optional for CustomInstall.__init__ (for gui changes) 2021-02-09 20:22:24 -08:00
30 changed files with 1070 additions and 273 deletions

9
.gitignore vendored
View File

@@ -1,6 +1,5 @@
.vscode/ .vscode/
bin/linux/save3ds_fuse bin/linux/save3ds_fuse
**/finalize
cstins/ cstins/
testing-class.py testing-class.py
@@ -12,9 +11,15 @@ testing-class.py
venv/ venv/
**/__pycache__/ **/__pycache__/
*.pyc *.pyc
*.egg-info/
# JetBrains # JetBrains
.idea/ .idea/
======= =======
*.pyc /build/
/dist/
/custom-install-finalize.3dsx
result
result-*

9
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,9 @@
## This is my personal project
I make this project in my free time whenever I feel like it. I make no promises about reading issues or pull requests on a timely basis, or that I will fix certain issues or merge pull requests (soon or ever).
If you are making a significant addition and you intend for it to be implemented in my repository, you should talk to me first, because putting it in my repo means I have to maintain it. Please keep in mind the above paragraph. Maybe keep your own fork if you need something.
## No AI-generated content
This project, like all my projects, employs a strict zero-tolarance policy against any content generated by artificial intelligence, for any reason. Do not use it for issues, pull requests, comments, or anything else. Any content found to be the result of generative AI will be deleted, and the user likely blocked.

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2019-2021 Ian Burgwin Copyright (c) 2019 Ian Burgwin
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

2
MANIFEST.in Normal file
View File

@@ -0,0 +1,2 @@
recursive-include custominstall/bin/*
include custominstall/title.db.gz

View File

@@ -15,7 +15,7 @@ Installs a title directly to an SD card for the Nintendo 3DS. Originally created
Note for Windows users: Enabling "Add Python 3.X to PATH" is **NOT** required! Python is installed with the `py` launcher by default. Note for Windows users: Enabling "Add Python 3.X to PATH" is **NOT** required! Python is installed with the `py` launcher by default.
1. [Dump boot9.bin and movable.sed](https://ihaveamac.github.io/dump.html) from a 3DS system. 1. [Dump boot9.bin and movable.sed](https://ihaveamac.github.io/dump.html) from a 3DS system.
2. Download the repo ([zip link](https://github.com/ihaveamac/custom-install/archive/module-newer-gui.zip) or `git clone`) 2. Download the repo ([zip link](https://github.com/ihaveamac/custom-install/archive/safe-install.zip) or `git clone`)
3. Install the packages: 3. Install the packages:
* Windows: Double-click `windows-install-dependencies.py` * Windows: Double-click `windows-install-dependencies.py`
* Alternate manual method: `py -3 -m pip install --user -r requirements-win32.txt` * Alternate manual method: `py -3 -m pip install --user -r requirements-win32.txt`
@@ -24,7 +24,7 @@ Note for Windows users: Enabling "Add Python 3.X to PATH" is **NOT** required! P
5. Download and use [custom-install-finalize](https://github.com/ihaveamac/custom-install/releases) on the 3DS system to finish the install. 5. Download and use [custom-install-finalize](https://github.com/ihaveamac/custom-install/releases) on the 3DS system to finish the install.
## Setup ## Setup
Linux users must build [wwylele/save3ds](https://github.com/wwylele/save3ds) and place `save3ds_fuse` in `bin/linux`. Install [rust using rustup](https://www.rust-lang.org/tools/install), then compile with: `cargo build`. The compiled binary is located in `target/debug/save3ds_fuse`, copy it to `bin/linux`. Linux users must build [wwylele/save3ds](https://github.com/wwylele/save3ds) and place `save3ds_fuse` in `bin/linux`. Install [rust using rustup](https://www.rust-lang.org/tools/install), then compile with: `cargo build --release --no-default-features`. The compiled binary is located in `target/release/save3ds_fuse`, copy it to `bin/linux`.
movable.sed is required and can be provided with `-m` or `--movable`. movable.sed is required and can be provided with `-m` or `--movable`.

View File

@@ -1,8 +0,0 @@
save3ds_fuse for win32 and darwin built with commit 25f0b5ec2600ddff8f5d2acf7c89ac1c4e743972
in repository https://github.com/wwylele/save3ds
win32 binary built on Windows 10, version 1903 64-bit with `cargo build --release --target=i686-pc-windows-msvc`.
darwin binary built on macOS 10.15.1 with `cd save3ds_fuse && cargo build --no-default-features --release`.
linux binary must be provided by the user.

Binary file not shown.

Binary file not shown.

10
custominstall/__init__.py Normal file
View File

@@ -0,0 +1,10 @@
# This file is a part of custom-install.
#
# Copyright (c) 2019 Ian Burgwin
# This file is licensed under The MIT License (MIT).
# You can find the full license text in LICENSE.md in the root of this project.
__author__ = 'ihaveahax'
__copyright__ = 'Copyright (c) 2019 Ian Burgwin'
__license__ = 'MIT'
__version__ = '2.1'

View File

@@ -1,17 +1,21 @@
#!/usr/bin/env python3
# This file is a part of custom-install.py. # This file is a part of custom-install.py.
# #
# custom-install is copyright (c) 2019-2020 Ian Burgwin # custom-install is copyright (c) 2019 Ian Burgwin
# This file is licensed under The MIT License (MIT). # This file is licensed under The MIT License (MIT).
# You can find the full license text in LICENSE.md in the root of this project. # You can find the full license text in LICENSE.md in the root of this project.
from argparse import ArgumentParser from argparse import ArgumentParser
from os import makedirs, rename, scandir from enum import Enum
from glob import glob
import gzip
from os import makedirs, rename, scandir, environ
from os.path import dirname, join, isdir, isfile from os.path import dirname, join, isdir, isfile
from random import randint from random import randint
from hashlib import sha256 from hashlib import sha256
from locale import getpreferredencoding
from pprint import pformat from pprint import pformat
from shutil import copyfile, copy2, rmtree from shutil import copyfile, copy2, rmtree, which
import sys import sys
from sys import platform, executable from sys import platform, executable
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
@@ -21,7 +25,7 @@ import subprocess
if TYPE_CHECKING: if TYPE_CHECKING:
from os import PathLike from os import PathLike
from typing import List, Union from typing import List, Union, Tuple
from events import Events from events import Events
@@ -32,6 +36,8 @@ from pyctr.type.ncch import NCCHSection
from pyctr.type.tmd import TitleMetadataError from pyctr.type.tmd import TitleMetadataError
from pyctr.util import roundup from pyctr.util import roundup
from . import __version__
if platform == 'msys': if platform == 'msys':
platform = 'win32' platform = 'win32'
@@ -42,16 +48,28 @@ if is_windows:
else: else:
from os import statvfs from os import statvfs
CI_VERSION = '2.1b1'
# used to run the save3ds_fuse binary next to the script
frozen = getattr(sys, 'frozen', False)
script_dir: str script_dir: str
frozen = getattr(sys, 'frozen', False)
if frozen: if frozen:
script_dir = dirname(executable) script_dir = dirname(executable)
else: else:
script_dir = dirname(__file__) script_dir = dirname(__file__)
# used to run the save3ds_fuse binary next to the script
if 'CUSTOM_INSTALL_SAVE3DS_PATH' in environ:
save3ds_fuse_path = environ['CUSTOM_INSTALL_SAVE3DS_PATH']
else:
save3ds_fuse_name = 'save3ds_fuse'
if is_windows:
save3ds_fuse_name += '.exe'
if frozen:
save3ds_fuse_path = join(script_dir, 'bin', save3ds_fuse_name)
else:
save3ds_fuse_path = join(script_dir, 'bin', platform, save3ds_fuse_name)
if not isfile(save3ds_fuse_path):
save3ds_fuse_path = which('save3ds_fuse')
# missing contents are replaced with 0xFFFFFFFF in the cmd file # missing contents are replaced with 0xFFFFFFFF in the cmd file
CMD_MISSING = b'\xff\xff\xff\xff' CMD_MISSING = b'\xff\xff\xff\xff'
@@ -74,6 +92,15 @@ class InvalidCIFinishError(Exception):
pass pass
class InstallStatus(Enum):
Waiting = 0
Starting = 1
Writing = 2
Finishing = 3
Done = 4
Failed = 5
def get_free_space(path: 'Union[PathLike, bytes, str]'): def get_free_space(path: 'Union[PathLike, bytes, str]'):
if is_windows: if is_windows:
lpSectorsPerCluster = c_ulonglong(0) lpSectorsPerCluster = c_ulonglong(0)
@@ -191,15 +218,15 @@ def get_install_size(title: 'Union[CIAReader, CDNReader]'):
class CustomInstall: class CustomInstall:
def __init__(self, boot9, seeddb, movable, sd, cifinish_out=None, def __init__(self, *, movable, sd, cifinish_out=None, overwrite_saves=False, skip_contents=False,
overwrite_saves=False, skip_contents=False): boot9=None, seeddb=None):
self.event = Events() self.event = Events()
self.log_lines = [] # Stores all info messages for user to view self.log_lines = [] # Stores all info messages for user to view
self.crypto = CryptoEngine(boot9=boot9) self.crypto = CryptoEngine(boot9=boot9)
self.crypto.setup_sd_key_from_file(movable) self.crypto.setup_sd_key_from_file(movable)
self.seeddb = seeddb self.seeddb = seeddb
self.readers: 'List[Union[CDNReader, CIAReader]]' = [] self.readers: 'List[Tuple[Union[CDNReader, CIAReader], Union[PathLike, bytes, str]]]' = []
self.sd = sd self.sd = sd
self.skip_contents = skip_contents self.skip_contents = skip_contents
self.overwrite_saves = overwrite_saves self.overwrite_saves = overwrite_saves
@@ -238,6 +265,9 @@ class CustomInstall:
return reader return reader
def prepare_titles(self, paths: 'List[PathLike]'): def prepare_titles(self, paths: 'List[PathLike]'):
if self.seeddb:
load_seeddb(self.seeddb)
readers = [] readers = []
for path in paths: for path in paths:
self.log(f'Reading {path}') self.log(f'Reading {path}')
@@ -249,27 +279,25 @@ class CustomInstall:
if reader.tmd.title_id.startswith('00048'): # DSiWare if reader.tmd.title_id.startswith('00048'): # DSiWare
self.log(f'Skipping {reader.tmd.title_id} - DSiWare is not supported') self.log(f'Skipping {reader.tmd.title_id} - DSiWare is not supported')
continue continue
readers.append(reader) readers.append((reader, path))
self.readers = readers self.readers = readers
def check_size(self): def check_size(self):
total_size = 0 total_size = 0
for r in self.readers: for r, _ in self.readers:
total_size += get_install_size(r) total_size += get_install_size(r)
free_space = get_free_space(self.sd) free_space = get_free_space(self.sd)
return total_size, free_space return total_size, free_space
def check_for_id0(self):
sd_path = join(self.sd, 'Nintendo 3DS', self.crypto.id0.hex())
return isdir(sd_path)
def start(self): def start(self):
if frozen: if not (save3ds_fuse_path and isfile(save3ds_fuse_path)):
save3ds_fuse_path = join(script_dir, 'bin', 'save3ds_fuse')
else:
save3ds_fuse_path = join(script_dir, 'bin', platform, 'save3ds_fuse')
if is_windows:
save3ds_fuse_path += '.exe'
if not isfile(save3ds_fuse_path):
self.log("Couldn't find " + save3ds_fuse_path, 2) self.log("Couldn't find " + save3ds_fuse_path, 2)
return None, False return None, False, 0
crypto = self.crypto crypto = self.crypto
# TODO: Move a lot of these into their own methods # TODO: Move a lot of these into their own methods
@@ -280,6 +308,8 @@ class CustomInstall:
f'please remove extra directories') f'please remove extra directories')
elif len(id1s) == 0: elif len(id1s) == 0:
raise SDPathError(f'Could not find a suitable id1 directory for id0 {crypto.id0.hex()}') raise SDPathError(f'Could not find a suitable id1 directory for id0 {crypto.id0.hex()}')
id1 = id1s[0]
sd_path = join(sd_path, id1)
if self.cifinish_out: if self.cifinish_out:
cifinish_path = self.cifinish_out cifinish_path = self.cifinish_out
@@ -294,7 +324,41 @@ class CustomInstall:
f'This could mean an issue with the SD card or the filesystem. Please check it for errors.\n' f'This could mean an issue with the SD card or the filesystem. Please check it for errors.\n'
f'It is also possible, though less likely, to be an issue with custom-install.\n' f'It is also possible, though less likely, to be an issue with custom-install.\n'
f'Exiting now to prevent possible issues. If you want to try again, delete cifinish.bin from the SD card and re-run custom-install.') f'Exiting now to prevent possible issues. If you want to try again, delete cifinish.bin from the SD card and re-run custom-install.')
return None, False return None, False, 0
db_path = join(sd_path, 'dbs')
titledb_path = join(db_path, 'title.db')
importdb_path = join(db_path, 'import.db')
if not isfile(titledb_path):
makedirs(db_path, exist_ok=True)
with gzip.open(join(script_dir, 'title.db.gz')) as f:
tdb = f.read()
self.log(f'Creating title.db...')
with open(titledb_path, 'wb') as o:
with self.crypto.create_ctr_io(Keyslot.SD, o, self.crypto.sd_path_to_iv('/dbs/title.db')) as e:
e.write(tdb)
cmac = crypto.create_cmac_object(Keyslot.CMACSDNAND)
cmac_data = [b'CTR-9DB0', 0x2.to_bytes(4, 'little'), tdb[0x100:0x200]]
cmac.update(sha256(b''.join(cmac_data)).digest())
e.seek(0)
e.write(cmac.digest())
self.log(f'Creating import.db...')
with open(importdb_path, 'wb') as o:
with self.crypto.create_ctr_io(Keyslot.SD, o, self.crypto.sd_path_to_iv('/dbs/import.db')) as e:
e.write(tdb)
cmac = crypto.create_cmac_object(Keyslot.CMACSDNAND)
cmac_data = [b'CTR-9DB0', 0x3.to_bytes(4, 'little'), tdb[0x100:0x200]]
cmac.update(sha256(b''.join(cmac_data)).digest())
e.seek(0)
e.write(cmac.digest())
del tdb
with TemporaryDirectory(suffix='-custom-install') as tempdir: with TemporaryDirectory(suffix='-custom-install') as tempdir:
# set up the common arguments for the two times we call save3ds_fuse # set up the common arguments for the two times we call save3ds_fuse
@@ -317,7 +381,7 @@ class CustomInstall:
out = subprocess.run(save3ds_fuse_common_args + ['-x'], out = subprocess.run(save3ds_fuse_common_args + ['-x'],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
encoding=getpreferredencoding(), encoding='utf-8',
**extra_kwargs) **extra_kwargs)
if out.returncode: if out.returncode:
for l in out.stdout.split('\n'): for l in out.stdout.split('\n'):
@@ -325,18 +389,16 @@ class CustomInstall:
self.log('Command line:') self.log('Command line:')
for l in pformat(out.args).split('\n'): for l in pformat(out.args).split('\n'):
self.log(l) self.log(l)
return None, False return None, False, 0
sd_path = join(sd_path, id1s[0])
load_seeddb(self.seeddb)
install_state = {'installed': [], 'failed': []} install_state = {'installed': [], 'failed': []}
# Now loop through all provided cia files # Now loop through all provided cia files
for idx, cia in enumerate(self.readers): for idx, info in enumerate(self.readers):
cia, path = info
self.event.on_cia_start(idx) self.event.on_cia_start(idx)
self.event.update_status(path, InstallStatus.Starting)
temp_title_root = join(self.sd, f'ci-install-temp-{cia.tmd.title_id}-{randint(0, 0xFFFFFFFF):08x}') temp_title_root = join(self.sd, f'ci-install-temp-{cia.tmd.title_id}-{randint(0, 0xFFFFFFFF):08x}')
makedirs(temp_title_root, exist_ok=True) makedirs(temp_title_root, exist_ok=True)
@@ -383,6 +445,7 @@ class CustomInstall:
temp_content_root = join(temp_title_root, 'content') temp_content_root = join(temp_title_root, 'content')
if not self.skip_contents: if not self.skip_contents:
self.event.update_status(path, InstallStatus.Writing)
makedirs(join(temp_content_root, 'cmd'), exist_ok=True) makedirs(join(temp_content_root, 'cmd'), exist_ok=True)
if cia.tmd.save_size: if cia.tmd.save_size:
makedirs(join(temp_title_root, 'data'), exist_ok=True) makedirs(join(temp_title_root, 'data'), exist_ok=True)
@@ -418,12 +481,13 @@ class CustomInstall:
self.log(f'Writing {content_enc_path}...') self.log(f'Writing {content_enc_path}...')
with cia.open_raw_section(co.cindex) as s, open(content_out_path, 'wb') as o: with cia.open_raw_section(co.cindex) as s, open(content_out_path, 'wb') as o:
result_hash = self.copy_with_progress(s, o, co.size, content_enc_path) result_hash = self.copy_with_progress(s, o, co.size, content_enc_path)
if result_hash != co.hash: if result_hash != co.hash:
self.log(f'WARNING: Hash does not match for {content_enc_path}!') self.log(f'WARNING: Hash does not match for {content_enc_path}!')
install_state['failed'].append(display_title) install_state['failed'].append(display_title)
rename(temp_title_root, temp_title_root + '-corrupted') rename(temp_title_root, temp_title_root + '-corrupted')
do_continue = True do_continue = True
break self.event.update_status(path, InstallStatus.Failed)
break
if do_continue: if do_continue:
continue continue
@@ -534,6 +598,7 @@ class CustomInstall:
b'\0' * 0x2c b'\0' * 0x2c
] ]
self.event.update_status(path, InstallStatus.Finishing)
if isdir(title_root): if isdir(title_root):
self.log(f'Removing original install at {title_root}...') self.log(f'Removing original install at {title_root}...')
rmtree(title_root) rmtree(title_root)
@@ -554,7 +619,7 @@ class CustomInstall:
out = subprocess.run(save3ds_fuse_common_args + ['-i'], out = subprocess.run(save3ds_fuse_common_args + ['-i'],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
encoding=getpreferredencoding(), encoding='utf-8',
**extra_kwargs) **extra_kwargs)
if out.returncode: if out.returncode:
for l in out.stdout.split('\n'): for l in out.stdout.split('\n'):
@@ -563,11 +628,19 @@ class CustomInstall:
for l in pformat(out.args).split('\n'): for l in pformat(out.args).split('\n'):
self.log(l) self.log(l)
install_state['failed'].append(display_title) install_state['failed'].append(display_title)
self.event.update_status(path, InstallStatus.Failed)
install_state['installed'].append(display_title) else:
install_state['installed'].append(display_title)
self.event.update_status(path, InstallStatus.Done)
copied = False copied = False
# launchable applications, not DLC or update data
application_count = len(glob(join(tempdir, '00040000*')))
if install_state['installed']: if install_state['installed']:
if application_count >= 300:
self.log(f'{application_count} installed applications were detected.', 1)
self.log('The HOME Menu will only show 300 icons.', 1)
self.log('Some applications (not updates or DLC) will need to be deleted.', 1)
finalize_3dsx_orig_path = join(script_dir, 'custom-install-finalize.3dsx') finalize_3dsx_orig_path = join(script_dir, 'custom-install-finalize.3dsx')
hb_dir = join(self.sd, '3ds') hb_dir = join(self.sd, '3ds')
finalize_3dsx_path = join(hb_dir, 'custom-install-finalize.3dsx') finalize_3dsx_path = join(hb_dir, 'custom-install-finalize.3dsx')
@@ -583,7 +656,7 @@ class CustomInstall:
if copied: if copied:
self.log('custom-install-finalize has been copied to the SD card.') self.log('custom-install-finalize has been copied to the SD card.')
return install_state, copied return install_state, copied, application_count
def get_sd_path(self): def get_sd_path(self):
sd_path = join(self.sd, 'Nintendo 3DS', self.crypto.id0.hex()) sd_path = join(self.sd, 'Nintendo 3DS', self.crypto.id0.hex())
@@ -627,7 +700,7 @@ class CustomInstall:
return msg_with_type return msg_with_type
if __name__ == "__main__": def main():
parser = ArgumentParser(description='Install a CIA to the SD card for a Nintendo 3DS system.') parser = ArgumentParser(description='Install a CIA to the SD card for a Nintendo 3DS system.')
parser.add_argument('cia', help='CIA files', nargs='+') parser.add_argument('cia', help='CIA files', nargs='+')
parser.add_argument('-m', '--movable', help='movable.sed file', required=True) parser.add_argument('-m', '--movable', help='movable.sed file', required=True)
@@ -638,7 +711,7 @@ if __name__ == "__main__":
parser.add_argument('--overwrite-saves', help='overwrite existing save files', action='store_true') parser.add_argument('--overwrite-saves', help='overwrite existing save files', action='store_true')
parser.add_argument('--cifinish-out', help='path for cifinish.bin file, defaults to (SD root)/cifinish.bin') parser.add_argument('--cifinish-out', help='path for cifinish.bin file, defaults to (SD root)/cifinish.bin')
print(f'custom-install {CI_VERSION} - https://github.com/ihaveamac/custom-install') print(f'custom-install {__version__} - https://github.com/ihaveamac/custom-install')
args = parser.parse_args() args = parser.parse_args()
installer = CustomInstall(boot9=args.boot9, installer = CustomInstall(boot9=args.boot9,
@@ -664,6 +737,10 @@ if __name__ == "__main__":
installer.event.update_percentage += percent_handle installer.event.update_percentage += percent_handle
installer.event.on_error += error installer.event.on_error += error
if not installer.check_for_id0():
installer.event.on_error(f'Could not find id0 directory {installer.crypto.id0.hex()} '
f'inside Nintendo 3DS directory.')
installer.prepare_titles(args.cia) installer.prepare_titles(args.cia)
if not args.skip_contents: if not args.skip_contents:
@@ -674,7 +751,15 @@ if __name__ == "__main__":
f'Free space: {free_space / (1024 * 1024):0.2f} MiB') f'Free space: {free_space / (1024 * 1024):0.2f} MiB')
sys.exit(1) sys.exit(1)
result, copied_3dsx = installer.start() result, copied_3dsx, application_count = installer.start()
if result is False: if result is False:
# save3ds_fuse failed # save3ds_fuse failed
installer.log('NOTE: Once save3ds_fuse is fixed, run the same command again with --skip-contents') installer.log('NOTE: Once save3ds_fuse is fixed, run the same command again with --skip-contents')
if application_count >= 300:
installer.log(f'\n\nWarning: {application_count} installed applications were detected.\n'
f'The HOME Menu will only show 300 icons.\n'
f'Some applications (not updates or DLC) will need to be deleted.')
if __name__ == "__main__":
main()

View File

@@ -2,39 +2,49 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "aes" name = "aes"
version = "0.3.2" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "aes-soft 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "aesni 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "aes-soft" name = "aes-soft"
version = "0.3.3" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "aesni" name = "aesni"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.2.17" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -42,23 +52,28 @@ name = "autocfg"
version = "0.1.7" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.7.3" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "block-cipher-trait" name = "block-cipher"
version = "0.6.2" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -80,7 +95,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"byte_struct_derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "byte_struct_derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -95,73 +110,75 @@ dependencies = [
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.3.2" version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c2-chacha"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.10" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cmac" name = "chrono"
version = "0.2.0" version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"dbl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cmac"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dbl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "const-random" name = "const-random"
version = "0.1.6" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "const-random-macro" name = "const-random-macro"
version = "0.1.6" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "crypto-mac" name = "crypto-mac"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "dbl" name = "dbl"
version = "0.2.1" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.8.1" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -174,27 +191,20 @@ name = "fuse"
version = "0.3.1" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.12.3" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
] "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "generic-array"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -202,17 +212,17 @@ name = "getopts"
version = "0.2.21" version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.13" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -220,25 +230,31 @@ name = "hashbrown"
version = "0.6.3" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"ahash 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.65" version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libsave3ds" name = "libsave3ds"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "aes 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byte_struct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte_struct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cmac 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lru 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "lru 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -259,12 +275,34 @@ dependencies = [
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.4.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
version = "0.2.3" version = "0.2.3"
@@ -277,18 +315,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.6" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.11" version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
@@ -298,14 +331,6 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "proc-macro2"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "0.6.13" version = "0.6.13"
@@ -314,32 +339,24 @@ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@@ -348,7 +365,7 @@ name = "rand_core"
version = "0.5.1" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -364,31 +381,52 @@ name = "redox_syscall"
version = "0.1.56" version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "save3ds_fuse" name = "save3ds_fuse"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fuse 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "fuse 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"libsave3ds 0.1.0", "libsave3ds 0.1.0",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "stderrlog 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.8.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "block-buffer 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "stderrlog"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "1.0.0" version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -402,13 +440,22 @@ dependencies = [
] ]
[[package]] [[package]]
name = "syn" name = "termcolor"
version = "1.0.8" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ]
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "termion"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -417,23 +464,31 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "time" name = "thread_local"
version = "0.1.42" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.11.2" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.6" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -442,13 +497,26 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unicode-xid" name = "unreachable"
version = "0.2.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.7.0" version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -465,67 +533,85 @@ name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" "checksum aes 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7001367fde4c768a19d1029f0a8be5abd9308e1119846d5bd9ad26297b8faf5"
"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" "checksum aes-soft 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4925647ee64e5056cf231608957ce7c81e12d6d6e316b9ce1404778cc1d35fa7"
"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" "checksum aesni 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d050d39b0b7688b3a3254394c3e30a9d66c41dcf9b05b0e2dbdc623f6505d264"
"checksum ahash 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "2f00e10d4814aa20900e7948174384f79f1317f24f0ba7494e735111653fc330" "checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum block-buffer 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dbcf92448676f82bb7a334c58bbce8b0d43580fb5362a9d608b18879d12a3d31"
"checksum block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa136449e765dc7faa244561ccae839c394048667929af599b5d931ebe7b7f10"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byte_struct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bde2e17424d6d3042b950f39de519dfd398c2e08adb1402d3fc10232a17564e" "checksum byte_struct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bde2e17424d6d3042b950f39de519dfd398c2e08adb1402d3fc10232a17564e"
"checksum byte_struct_derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7fb6eccde50afec044557d1f1b8776168b7040255390eefffb39fcfd1ab40b2e" "checksum byte_struct_derive 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7fb6eccde50afec044557d1f1b8776168b7040255390eefffb39fcfd1ab40b2e"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4a435124bcc292eba031f1f725d7abacdaf13cbf9f935450e8c45aa9e96cad" "checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
"checksum const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b641a8c9867e341f3295564203b1c250eb8ce6cb6126e007941f78c4d2ed7fe" "checksum cmac 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9f8f8ba8b9640e29213f152015694e78208e601adf91c72b698460633b15715"
"checksum const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" "checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
"checksum dbl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28dc203b75decc900220c4d9838e738d08413e663c26826ba92b669bed1d0795" "checksum crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dbl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2735145c3b9ba15f2d7a3ae8cdafcbc8c98a7bef7f62afe9d08bd99fbf7130de"
"checksum digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fuse 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e57070510966bfef93662a81cb8aa2b1c7db0964354fa9921434f04b9e8660" "checksum fuse 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e57070510966bfef93662a81cb8aa2b1c7db0964354fa9921434f04b9e8660"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980"
"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd"
"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" "checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" "checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead"
"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum lru 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26b0dca4ac5b5083c5169ab12205e6473df1c7659940e4978b94f363c6b54b22" "checksum lru 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28e0c685219cd60e49a2796bba7e4fe6523e10daca4fd721e84e7f905093d60c"
"checksum num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum sha2 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72377440080fd008550fe9b441e854e43318db116f90181eef92e9ae9aedab48"
"checksum stderrlog 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32e5ee9b90a5452c570a0b0ac1c99ae9498db7e56e33d74366de7f2a7add7f25"
"checksum subtle 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
"checksum termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905"
"checksum thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99" "checksum thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

11
custominstall/bin/README Normal file
View File

@@ -0,0 +1,11 @@
save3ds_fuse for win32 and darwin built with commit 568b0597b17da0c8cfbd345bab27176cd84bd883
in repository https://github.com/wwylele/save3ds
win32 binary built on Windows 10, version 21H2 64-bit with `cargo build --release --target=i686-pc-windows-msvc`.
darwin binary built on macOS 12.2 with:
* `cargo build --target=aarch64-apple-darwin --no-default-features --release`
* `cargo build --target=x86_64-apple-darwin --no-default-features --release`
* Then a universal binary is built: `lipo -create -output save3ds_fuse-universal2 target/aarch64-apple-darwin/release/save3ds_fuse target/x86_64-apple-darwin/release/save3ds_fuse`
linux binary must be provided by the user.

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,14 @@
#!/usr/bin/env python3
# This file is a part of custom-install.py. # This file is a part of custom-install.py.
# #
# custom-install is copyright (c) 2019-2020 Ian Burgwin # custom-install is copyright (c) 2019 Ian Burgwin
# This file is licensed under The MIT License (MIT). # This file is licensed under The MIT License (MIT).
# You can find the full license text in LICENSE.md in the root of this project. # You can find the full license text in LICENSE.md in the root of this project.
from os import environ, scandir from os import environ, scandir
from os.path import abspath, basename, dirname, join, isfile from os.path import abspath, basename, dirname, join, isfile
from sys import exc_info, platform import sys
from threading import Thread, Lock from threading import Thread, Lock
from time import strftime from time import strftime
from traceback import format_exception from traceback import format_exception
@@ -16,20 +18,29 @@ import tkinter.filedialog as fd
import tkinter.messagebox as mb import tkinter.messagebox as mb
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from pyctr.crypto import MissingSeedError, CryptoEngine, load_seeddb
from pyctr.crypto.engine import b9_paths from pyctr.crypto.engine import b9_paths
from pyctr.util import config_dirs from pyctr.util import config_dirs
from pyctr.type.cdn import CDNError from pyctr.type.cdn import CDNError
from pyctr.type.cia import CIAError from pyctr.type.cia import CIAError
from pyctr.type.tmd import TitleMetadataError from pyctr.type.tmd import TitleMetadataError
from custominstall import CustomInstall, CI_VERSION, load_cifinish, InvalidCIFinishError from . import __version__
from .__main__ import CustomInstall, load_cifinish, InvalidCIFinishError, InstallStatus, save3ds_fuse_path
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Dict, List from os import PathLike
from typing import Dict, List, Union
is_windows = platform == 'win32' frozen = getattr(sys, 'frozen', None)
is_windows = sys.platform == 'win32'
taskbar = None taskbar = None
if is_windows: if is_windows:
if frozen:
# attempt to fix loading tcl/tk when running from a path with non-latin characters
tkinter_path = dirname(tk.__file__)
tcl_path = join(tkinter_path, 'tcl8.6')
environ['TCL_LIBRARY'] = 'lib/tkinter/tcl8.6'
try: try:
import comtypes.client as cc import comtypes.client as cc
@@ -37,7 +48,7 @@ if is_windows:
taskbar = cc.CreateObject('{56FDF344-FD6D-11D0-958A-006097C9A090}', interface=tbl.ITaskbarList3) taskbar = cc.CreateObject('{56FDF344-FD6D-11D0-958A-006097C9A090}', interface=tbl.ITaskbarList3)
taskbar.HrInit() taskbar.HrInit()
except ModuleNotFoundError: except (ModuleNotFoundError, UnicodeEncodeError, AttributeError):
pass pass
file_parent = dirname(abspath(__file__)) file_parent = dirname(abspath(__file__))
@@ -70,6 +81,18 @@ default_b9_path = find_first_file(b9_paths)
default_seeddb_path = find_first_file(seeddb_paths) default_seeddb_path = find_first_file(seeddb_paths)
default_movable_sed_path = find_first_file([join(file_parent, 'movable.sed')]) default_movable_sed_path = find_first_file([join(file_parent, 'movable.sed')])
if default_seeddb_path:
load_seeddb(default_seeddb_path)
statuses = {
InstallStatus.Waiting: 'Waiting',
InstallStatus.Starting: 'Starting',
InstallStatus.Writing: 'Writing',
InstallStatus.Finishing: 'Finishing',
InstallStatus.Done: 'Done',
InstallStatus.Failed: 'Failed',
}
class ConsoleFrame(ttk.Frame): class ConsoleFrame(ttk.Frame):
def __init__(self, parent: tk.BaseWidget = None, starting_lines: 'List[str]' = None): def __init__(self, parent: tk.BaseWidget = None, starting_lines: 'List[str]' = None):
@@ -176,7 +199,8 @@ class TitleReadFailResults(tk.Toplevel):
class InstallResults(tk.Toplevel): class InstallResults(tk.Toplevel):
def __init__(self, parent: tk.Tk = None, *, install_state: 'Dict[str, List[str]]', copied_3dsx: bool): def __init__(self, parent: tk.Tk = None, *, install_state: 'Dict[str, List[str]]', copied_3dsx: bool,
application_count: int):
super().__init__(parent) super().__init__(parent)
self.parent = parent self.parent = parent
@@ -209,6 +233,11 @@ class InstallResults(tk.Toplevel):
if install_state['installed'] and copied_3dsx: if install_state['installed'] and copied_3dsx:
message += '\n\ncustom-install-finalize has been copied to the SD card.' message += '\n\ncustom-install-finalize has been copied to the SD card.'
if application_count >= 300:
message += (f'\n\nWarning: {application_count} installed applications were detected.\n'
f'The HOME Menu will only show 300 icons.\n'
f'Some applications (not updates or DLC) will need to be deleted.')
message_label = ttk.Label(outer_container, text=message) message_label = ttk.Label(outer_container, text=message)
message_label.grid(row=0, column=0, sticky=tk.NSEW, padx=10, pady=10) message_label.grid(row=0, column=0, sticky=tk.NSEW, padx=10, pady=10)
@@ -235,6 +264,7 @@ class InstallResults(tk.Toplevel):
class CustomInstallGUI(ttk.Frame): class CustomInstallGUI(ttk.Frame):
console = None console = None
b9_loaded = False
def __init__(self, parent: tk.Tk = None): def __init__(self, parent: tk.Tk = None):
super().__init__(parent) super().__init__(parent)
@@ -285,12 +315,14 @@ class CustomInstallGUI(ttk.Frame):
sd_selected.delete('1.0', tk.END) sd_selected.delete('1.0', tk.END)
sd_selected.insert(tk.END, f) sd_selected.insert(tk.END, f)
sd_msed_path = find_first_file([join(f, 'gm9', 'out', 'movable.sed'), join(f, 'movable.sed')]) for filename in ['boot9.bin', 'seeddb.bin', 'movable.sed']:
if sd_msed_path: path = auto_input_filename(self, f, filename)
self.log('Found movable.sed on SD card at ' + sd_msed_path) if filename == 'boot9.bin':
box = self.file_picker_textboxes['movable.sed'] self.check_b9_loaded()
box.delete('1.0', tk.END) self.enable_buttons()
box.insert(tk.END, sd_msed_path) if filename == 'seeddb.bin':
load_seeddb(path)
sd_type_label = ttk.Label(file_pickers, text='SD root') sd_type_label = ttk.Label(file_pickers, text='SD root')
sd_type_label.grid(row=0, column=0) sd_type_label.grid(row=0, column=0)
@@ -303,14 +335,27 @@ class CustomInstallGUI(ttk.Frame):
self.file_picker_textboxes['sd'] = sd_selected self.file_picker_textboxes['sd'] = sd_selected
def auto_input_filename(self, f, filename):
sd_msed_path = find_first_file(
[join(f, "gm9", "out", filename), join(f, "boot9strap", filename), join(f, filename)]
)
if sd_msed_path:
self.log('Found ' + filename + ' on SD card at ' + sd_msed_path)
if filename.endswith('bin'):
filename = filename.split('.')[0]
box = self.file_picker_textboxes[filename]
box.delete('1.0', tk.END)
box.insert(tk.END, sd_msed_path)
return sd_msed_path
# This feels so wrong. # This feels so wrong.
def create_required_file_picker(type_name, types, default, row): def create_required_file_picker(type_name, types, default, row, callback=lambda filename: None):
def internal_callback(): def internal_callback():
f = fd.askopenfilename(parent=parent, title='Select ' + type_name, filetypes=types, f = fd.askopenfilename(parent=parent, title='Select ' + type_name, filetypes=types,
initialdir=file_parent) initialdir=file_parent)
if f: if f:
selected.delete('1.0', tk.END) selected.delete('1.0', tk.END)
selected.insert(tk.END, f) selected.insert(tk.END, f)
callback(f)
type_label = ttk.Label(file_pickers, text=type_name) type_label = ttk.Label(file_pickers, text=type_name)
type_label.grid(row=row, column=0) type_label.grid(row=row, column=0)
@@ -325,8 +370,15 @@ class CustomInstallGUI(ttk.Frame):
self.file_picker_textboxes[type_name] = selected self.file_picker_textboxes[type_name] = selected
create_required_file_picker('boot9', [('boot9 file', '*.bin')], default_b9_path, 1) def b9_callback(path: 'Union[PathLike, bytes, str]'):
create_required_file_picker('seeddb', [('seeddb file', '*.bin')], default_seeddb_path, 2) self.check_b9_loaded()
self.enable_buttons()
def seeddb_callback(path: 'Union[PathLike, bytes, str]'):
load_seeddb(path)
create_required_file_picker('boot9', [('boot9 file', '*.bin')], default_b9_path, 1, b9_callback)
create_required_file_picker('seeddb', [('seeddb file', '*.bin')], default_seeddb_path, 2, seeddb_callback)
create_required_file_picker('movable.sed', [('movable.sed file', '*.sed')], default_movable_sed_path, 3) create_required_file_picker('movable.sed', [('movable.sed file', '*.sed')], default_movable_sed_path, 3)
# ---------------------------------------------------------------- # # ---------------------------------------------------------------- #
@@ -404,14 +456,16 @@ class CustomInstallGUI(ttk.Frame):
self.treeview = ttk.Treeview(treeview_frame, yscrollcommand=treeview_scrollbar.set) self.treeview = ttk.Treeview(treeview_frame, yscrollcommand=treeview_scrollbar.set)
self.treeview.grid(row=0, column=0, sticky=tk.NSEW) self.treeview.grid(row=0, column=0, sticky=tk.NSEW)
self.treeview.configure(columns=('filepath', 'titleid', 'titlename'), show='headings') self.treeview.configure(columns=('filepath', 'titleid', 'titlename', 'status'), show='headings')
self.treeview.column('filepath', width=200, anchor=tk.W) self.treeview.column('filepath', width=200, anchor=tk.W)
self.treeview.heading('filepath', text='File path') self.treeview.heading('filepath', text='File path')
self.treeview.column('titleid', width=50, anchor=tk.W) self.treeview.column('titleid', width=70, anchor=tk.W)
self.treeview.heading('titleid', text='Title ID') self.treeview.heading('titleid', text='Title ID')
self.treeview.column('titlename', width=150, anchor=tk.W) self.treeview.column('titlename', width=150, anchor=tk.W)
self.treeview.heading('titlename', text='Title name') self.treeview.heading('titlename', text='Title name')
self.treeview.column('status', width=20, anchor=tk.W)
self.treeview.heading('status', text='Status')
treeview_scrollbar.configure(command=self.treeview.yview) treeview_scrollbar.configure(command=self.treeview.yview)
@@ -446,15 +500,21 @@ class CustomInstallGUI(ttk.Frame):
self.status_label = ttk.Label(self, text='Waiting...') self.status_label = ttk.Label(self, text='Waiting...')
self.status_label.grid(row=5, column=0, sticky=tk.NSEW) self.status_label.grid(row=5, column=0, sticky=tk.NSEW)
self.log(f'custom-install {CI_VERSION} - https://github.com/ihaveamac/custom-install', status=False) self.log(f'custom-install {__version__} - https://github.com/ihaveamac/custom-install', status=False)
if is_windows and not taskbar: if is_windows and not taskbar:
self.log('Note: comtypes module not found.') self.log('Note: Could not load taskbar lib.')
self.log('Note: Progress will not be shown in the Windows taskbar.') self.log('Note: Progress will not be shown in the Windows taskbar.')
self.log('Ready.') self.log('Ready.')
self.disable_during_install = (add_cias, add_dirs, remove_selected, start, *self.file_picker_textboxes.values()) self.require_boot9 = (add_cias, add_cdn, add_dirs, remove_selected, start)
self.disable_buttons()
self.check_b9_loaded()
self.enable_buttons()
if not self.b9_loaded:
self.log('Note: boot9 was not auto-detected. Please choose it before adding any titles.')
def sort_treeview(self): def sort_treeview(self):
l = [(self.treeview.set(k, 'titlename'), k) for k in self.treeview.get_children()] l = [(self.treeview.set(k, 'titlename'), k) for k in self.treeview.get_children()]
@@ -464,7 +524,23 @@ class CustomInstallGUI(ttk.Frame):
for idx, pair in enumerate(l): for idx, pair in enumerate(l):
self.treeview.move(pair[1], '', idx) self.treeview.move(pair[1], '', idx)
def check_b9_loaded(self):
if not self.b9_loaded:
boot9 = self.file_picker_textboxes['boot9'].get('1.0', tk.END).strip()
try:
tmp_crypto = CryptoEngine(boot9=boot9)
self.b9_loaded = tmp_crypto.b9_keys_set
except:
return False
return self.b9_loaded
def update_status(self, path: 'Union[PathLike, bytes, str]', status: InstallStatus):
self.treeview.set(path, 'status', statuses[status])
def add_cia(self, path): def add_cia(self, path):
if not self.check_b9_loaded():
# this shouldn't happen
return False, 'Please choose boot9 first'
path = abspath(path) path = abspath(path)
if path in self.readers: if path in self.readers:
return False, 'File already in list' return False, 'File already in list'
@@ -472,6 +548,8 @@ class CustomInstallGUI(ttk.Frame):
reader = CustomInstall.get_reader(path) reader = CustomInstall.get_reader(path)
except (CIAError, CDNError, TitleMetadataError): except (CIAError, CDNError, TitleMetadataError):
return False, 'Failed to read as a CIA or CDN title, probably corrupt' return False, 'Failed to read as a CIA or CDN title, probably corrupt'
except MissingSeedError:
return False, 'Latest seeddb.bin is required, check the README for details'
except Exception as e: except Exception as e:
return False, f'Exception occurred: {type(e).__name__}: {e}' return False, f'Exception occurred: {type(e).__name__}: {e}'
@@ -481,7 +559,8 @@ class CustomInstallGUI(ttk.Frame):
title_name = reader.contents[0].exefs.icon.get_app_title().short_desc title_name = reader.contents[0].exefs.icon.get_app_title().short_desc
except: except:
title_name = '(No title)' title_name = '(No title)'
self.treeview.insert('', tk.END, text=path, iid=path, values=(path, reader.tmd.title_id, title_name)) self.treeview.insert('', tk.END, text=path, iid=path,
values=(path, reader.tmd.title_id, title_name, statuses[InstallStatus.Waiting]))
self.readers[path] = reader self.readers[path] = reader
return True, '' return True, ''
@@ -532,25 +611,26 @@ class CustomInstallGUI(ttk.Frame):
mb.showinfo('Info', message, parent=self.parent) mb.showinfo('Info', message, parent=self.parent)
def disable_buttons(self): def disable_buttons(self):
for b in self.disable_during_install: for b in self.require_boot9:
b.config(state=tk.DISABLED)
for b in self.file_picker_textboxes.values():
b.config(state=tk.DISABLED) b.config(state=tk.DISABLED)
def enable_buttons(self): def enable_buttons(self):
for b in self.disable_during_install: if self.b9_loaded:
for b in self.require_boot9:
b.config(state=tk.NORMAL)
for b in self.file_picker_textboxes.values():
b.config(state=tk.NORMAL) b.config(state=tk.NORMAL)
def start_install(self): def start_install(self):
sd_root = self.file_picker_textboxes['sd'].get('1.0', tk.END).strip() sd_root = self.file_picker_textboxes['sd'].get('1.0', tk.END).strip()
boot9 = self.file_picker_textboxes['boot9'].get('1.0', tk.END).strip()
seeddb = self.file_picker_textboxes['seeddb'].get('1.0', tk.END).strip() seeddb = self.file_picker_textboxes['seeddb'].get('1.0', tk.END).strip()
movable_sed = self.file_picker_textboxes['movable.sed'].get('1.0', tk.END).strip() movable_sed = self.file_picker_textboxes['movable.sed'].get('1.0', tk.END).strip()
if not sd_root: if not sd_root:
self.show_error('SD root is not specified.') self.show_error('SD root is not specified.')
return return
if not boot9:
self.show_error('boot9 is not specified.')
return
if not movable_sed: if not movable_sed:
self.show_error('movable.sed is not specified.') self.show_error('movable.sed is not specified.')
return return
@@ -560,29 +640,37 @@ class CustomInstallGUI(ttk.Frame):
'Continue?'): 'Continue?'):
return return
self.disable_buttons()
if not len(self.readers): if not len(self.readers):
self.show_error('There are no titles added to install.') self.show_error('There are no titles added to install.')
return return
self.log('Starting install...') for path in self.readers.keys():
self.update_status(path, InstallStatus.Waiting)
self.disable_buttons()
if taskbar: if taskbar:
taskbar.SetProgressState(self.hwnd, tbl.TBPF_NORMAL) taskbar.SetProgressState(self.hwnd, tbl.TBPF_NORMAL)
installer = CustomInstall(boot9=boot9, installer = CustomInstall(movable=movable_sed,
seeddb=seeddb,
movable=movable_sed,
sd=sd_root, sd=sd_root,
skip_contents=self.skip_contents_var.get() == 1, skip_contents=self.skip_contents_var.get() == 1,
overwrite_saves=self.overwrite_saves_var.get() == 1) overwrite_saves=self.overwrite_saves_var.get() == 1)
if not installer.check_for_id0():
self.show_error(f'id0 {installer.crypto.id0.hex()} was not found inside "Nintendo 3DS" on the SD card.\n'
f'\n'
f'Before using custom-install, you should use this SD card on the appropriate console.\n'
f'\n'
f'Otherwise, make sure the correct movable.sed is being used.')
return
self.log('Starting install...')
# use the treeview which has been sorted alphabetically # use the treeview which has been sorted alphabetically
#installer.readers = self.readers.values()
readers_final = [] readers_final = []
for k in self.treeview.get_children(): for k in self.treeview.get_children():
readers_final.append(self.readers[self.treeview.set(k, 'filepath')]) filepath = self.treeview.set(k, 'filepath')
readers_final.append((self.readers[filepath], filepath))
installer.readers = readers_final installer.readers = readers_final
@@ -618,6 +706,7 @@ class CustomInstallGUI(ttk.Frame):
installer.event.update_percentage += ci_update_percentage installer.event.update_percentage += ci_update_percentage
installer.event.on_error += ci_on_error installer.event.on_error += ci_on_error
installer.event.on_cia_start += ci_on_cia_start installer.event.on_cia_start += ci_on_cia_start
installer.event.update_status += self.update_status
if self.skip_contents_var.get() != 1: if self.skip_contents_var.get() != 1:
total_size, free_space = installer.check_size() total_size, free_space = installer.check_size()
@@ -630,24 +719,36 @@ class CustomInstallGUI(ttk.Frame):
def install(): def install():
try: try:
result, copied_3dsx = installer.start() result, copied_3dsx, application_count = installer.start()
if result: if result:
result_window = InstallResults(self.parent, install_state=result, copied_3dsx=copied_3dsx) result_window = InstallResults(self.parent,
install_state=result,
copied_3dsx=copied_3dsx,
application_count=application_count)
result_window.focus() result_window.focus()
elif result is None: elif result is None:
self.show_error("An error occurred when trying to run save3ds_fuse.\n" self.show_error("An error occurred when trying to run save3ds_fuse.\n"
"Either title.db doesn't exist, or save3ds_fuse couldn't be run.") "Either title.db doesn't exist, or save3ds_fuse couldn't be run.")
self.open_console() self.open_console()
except: except:
installer.event.on_error(exc_info()) installer.event.on_error(sys.exc_info())
finally: finally:
self.enable_buttons() self.enable_buttons()
Thread(target=install).start() Thread(target=install).start()
window = tk.Tk() def main():
window.title(f'custom-install {CI_VERSION}') if not (save3ds_fuse_path and isfile(save3ds_fuse_path)):
frame = CustomInstallGUI(window) mb.showerror('Error', "Couldn't find save3ds_fuse. Please place it PATH.")
frame.pack(fill=tk.BOTH, expand=True) return
window.mainloop()
window = tk.Tk()
window.title(f'custom-install {__version__}')
frame = CustomInstallGUI(window)
frame.pack(fill=tk.BOTH, expand=True)
window.mainloop()
if __name__ == '__main__':
main()

BIN
custominstall/title.db.gz Normal file

Binary file not shown.

34
default.nix Normal file
View File

@@ -0,0 +1,34 @@
{
pkgs ? import <nixpkgs> { },
# just so i can use the same pinned version as the flake...
pyctr ? (
let
flakeLock = builtins.fromJSON (builtins.readFile ./flake.lock);
pyctr-repo = import (builtins.fetchTarball (
with flakeLock.nodes.pyctr.locked;
{
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
}
)) { inherit pkgs; };
in
pyctr-repo.pyctr
),
save3ds ? (
let
flakeLock = builtins.fromJSON (builtins.readFile ./flake.lock);
hax-nur-repo = import (builtins.fetchTarball (
with flakeLock.nodes.hax-nur.locked;
{
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
}
)) { inherit pkgs; };
in
hax-nur-repo.save3ds
),
}:
rec {
custominstall = pkgs.python3Packages.callPackage ./package.nix {
inherit pyctr save3ds;
};
}

View File

@@ -54,7 +54,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations \
-fomit-frame-pointer -ffunction-sections \ -fomit-frame-pointer -ffunction-sections \
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -DARM11 -D_3DS CFLAGS += $(INCLUDE) -D__3DS__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11

82
finalize/flake.lock generated Normal file
View File

@@ -0,0 +1,82 @@
{
"nodes": {
"devkitNix": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1766539742,
"narHash": "sha256-F6OeM2LrLo2n+Xg5XU4udQR/vuWWrDMKxXRzNXE2ClQ=",
"owner": "bandithedoge",
"repo": "devkitNix",
"rev": "c97f9880737716085e78009cba6bf85ad104628b",
"type": "github"
},
"original": {
"owner": "bandithedoge",
"repo": "devkitNix",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devkitNix": "devkitNix",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

31
finalize/flake.nix Normal file
View File

@@ -0,0 +1,31 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
devkitNix.url = "github:bandithedoge/devkitNix";
devkitNix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, devkitNix }: let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ devkitNix.overlays.default ]; };
in {
devShells.x86_64-linux = rec {
custom-install-finalize = pkgs.mkShell.override { stdenv = pkgs.devkitNix.stdenvARM; } {};
cif = custom-install-finalize;
};
packages.x86_64-linux = rec {
custom-install-finalize = pkgs.devkitNix.stdenvARM.mkDerivation rec {
name = "custom-install-finalize";
src = builtins.path { path = ./.; name = name; };
makeFlags = [ "TARGET=${name}" ];
installPhase = ''
mkdir $out
cp ${name}.3dsx $out
'';
};
cif = custom-install-finalize;
};
};
}

View File

@@ -197,31 +197,99 @@ fail:
return -1; return -1;
} }
Result check_title_exist(u64 title_id, u64 *ticket_ids, u32 ticket_ids_length, u64 *title_ids, u32 title_ids_length)
{
Result ret = -2;
for (u32 i = 0; i < ticket_ids_length; i++)
{
if (ticket_ids[i] == title_id)
{
ret++;
break;
}
}
for (u32 i = 0; i < title_ids_length; i++)
{
if (title_ids[i] == title_id)
{
ret++;
break;
}
}
return ret;
}
void finalize_install(void) void finalize_install(void)
{ {
Result res; Result res;
Handle ticketHandle; Handle ticketHandle;
struct ticket_dumb ticket_buf; struct ticket_dumb ticket_buf;
struct finish_db_entry_final *entries; struct finish_db_entry_final *entries = NULL;
int title_count; int title_count;
title_count = load_cifinish(CIFINISH_PATH, &entries); u32 titles_read;
if (title_count == -1) u32 tickets_read;
res = AM_GetTitleCount(MEDIATYPE_SD, &titles_read);
if (R_FAILED(res))
{ {
free(entries);
return; return;
} }
if (title_count == 0)
res = AM_GetTicketCount(&tickets_read);
if (R_FAILED(res))
{
return;
}
u64 *installed_ticket_ids = malloc(sizeof(u64) * tickets_read );
u64 *installed_title_ids = malloc(sizeof(u64) * titles_read );
res = AM_GetTitleList(&titles_read, MEDIATYPE_SD, titles_read, installed_title_ids);
if (R_FAILED(res))
{
goto exit;
}
res = AM_GetTicketList(&tickets_read, tickets_read, 0, installed_ticket_ids);
if (R_FAILED(res))
{
goto exit;
}
title_count = load_cifinish(CIFINISH_PATH, &entries);
if (title_count == -1)
{
goto exit;
}
else if (title_count == 0)
{ {
printf("No titles to finalize.\n"); printf("No titles to finalize.\n");
free(entries); goto exit;
return;
} }
memcpy(&ticket_buf, basetik_bin, basetik_bin_size); memcpy(&ticket_buf, basetik_bin, basetik_bin_size);
Result exist_res = 0;
for (int i = 0; i < title_count; ++i) for (int i = 0; i < title_count; ++i)
{ {
exist_res = check_title_exist(entries[i].title_id, installed_ticket_ids, tickets_read, installed_title_ids, titles_read);
if (R_SUCCEEDED(exist_res))
{
printf("No need to finalize %016llx, skipping...\n", entries[i].title_id);
continue;
}
printf("Finalizing %016llx...\n", entries[i].title_id); printf("Finalizing %016llx...\n", entries[i].title_id);
ticket_buf.title_id_be = __builtin_bswap64(entries[i].title_id); ticket_buf.title_id_be = __builtin_bswap64(entries[i].title_id);
@@ -231,8 +299,7 @@ void finalize_install(void)
{ {
printf("Failed to begin ticket install: %08lx\n", res); printf("Failed to begin ticket install: %08lx\n", res);
AM_InstallTicketAbort(ticketHandle); AM_InstallTicketAbort(ticketHandle);
free(entries); goto exit;
return;
} }
res = FSFILE_Write(ticketHandle, NULL, 0, &ticket_buf, sizeof(struct ticket_dumb), 0); res = FSFILE_Write(ticketHandle, NULL, 0, &ticket_buf, sizeof(struct ticket_dumb), 0);
@@ -240,8 +307,7 @@ void finalize_install(void)
{ {
printf("Failed to write ticket: %08lx\n", res); printf("Failed to write ticket: %08lx\n", res);
AM_InstallTicketAbort(ticketHandle); AM_InstallTicketAbort(ticketHandle);
free(entries); goto exit;
return;
} }
res = AM_InstallTicketFinish(ticketHandle); res = AM_InstallTicketFinish(ticketHandle);
@@ -249,8 +315,7 @@ void finalize_install(void)
{ {
printf("Failed to finish ticket install: %08lx\n", res); printf("Failed to finish ticket install: %08lx\n", res);
AM_InstallTicketAbort(ticketHandle); AM_InstallTicketAbort(ticketHandle);
free(entries); goto exit;
return;
} }
if (entries[i].has_seed) if (entries[i].has_seed)
@@ -267,7 +332,12 @@ void finalize_install(void)
printf("Deleting %s...\n", CIFINISH_PATH); printf("Deleting %s...\n", CIFINISH_PATH);
unlink(CIFINISH_PATH); unlink(CIFINISH_PATH);
exit:
free(entries); free(entries);
free(installed_ticket_ids);
free(installed_title_ids);
return;
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@@ -276,7 +346,7 @@ int main(int argc, char* argv[])
gfxInitDefault(); gfxInitDefault();
consoleInit(GFX_TOP, NULL); consoleInit(GFX_TOP, NULL);
printf("custom-install-finalize v1.5\n"); printf("custom-install-finalize v1.6\n");
finalize_install(); finalize_install();
// print this at the end in case it gets pushed off the screen // print this at the end in case it gets pushed off the screen

93
flake.lock generated Normal file
View File

@@ -0,0 +1,93 @@
{
"nodes": {
"hax-nur": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1767302708,
"narHash": "sha256-uCSEH/PR5/JxwuMayB4fMcOhOCT7I6BzWp7EtEYYjFQ=",
"owner": "ihaveamac",
"repo": "nur-packages",
"rev": "f612d64a4136c3a4820e37ed50cefb6460dde857",
"type": "github"
},
"original": {
"owner": "ihaveamac",
"ref": "master",
"repo": "nur-packages",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pyctr": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1763515957,
"narHash": "sha256-S0qzooGQN5tkbIVgijVZ9umvBC1dYbdPN97tks5SbwE=",
"owner": "ihaveamac",
"repo": "pyctr",
"rev": "eb8d4d06ce7339727d3f72b40f45ec3260336058",
"type": "github"
},
"original": {
"owner": "ihaveamac",
"ref": "master",
"repo": "pyctr",
"type": "github"
}
},
"root": {
"inputs": {
"hax-nur": "hax-nur",
"nixpkgs": "nixpkgs",
"pyctr": "pyctr"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"hax-nur",
"nixpkgs"
]
},
"locked": {
"lastModified": 1767122417,
"narHash": "sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "dec15f37015ac2e774c84d0952d57fcdf169b54d",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

47
flake.nix Normal file
View File

@@ -0,0 +1,47 @@
{
description = "custominstall";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
pyctr.url = "github:ihaveamac/pyctr/master";
pyctr.inputs.nixpkgs.follows = "nixpkgs";
hax-nur.url = "github:ihaveamac/nur-packages/master";
hax-nur.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
inputs@{
self,
nixpkgs,
pyctr,
hax-nur,
}:
let
systems = [
"x86_64-linux"
"i686-linux"
"x86_64-darwin"
"aarch64-darwin"
"aarch64-linux"
"armv6l-linux"
"armv7l-linux"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
in
{
legacyPackages = forAllSystems (
system:
(import ./default.nix {
pkgs = import nixpkgs { inherit system; };
pyctr = pyctr.packages.${system}.pyctr;
save3ds = hax-nur.packages.${system}.save3ds;
})
// {
default = self.legacyPackages.${system}.custominstall;
}
);
packages = forAllSystems (
system: nixpkgs.lib.filterAttrs (_: v: nixpkgs.lib.isDerivation v) self.legacyPackages.${system}
);
};
}

View File

@@ -1,11 +1,13 @@
mkdir build mkdir build
mkdir dist mkdir dist
cxfreeze ci-gui.py --target-dir=build\custom-install-standalone --base-name=Win32GUI python setup-cxfreeze.py build_exe --build-exe=build\custom-install-standalone
mkdir build\custom-install-standalone\bin mkdir build\custom-install-standalone\bin
copy TaskbarLib.tlb build\custom-install-standalone copy TaskbarLib.tlb build\custom-install-standalone
copy bin\win32\save3ds_fuse.exe build\custom-install-standalone\bin copy bin\win32\save3ds_fuse.exe build\custom-install-standalone\bin
copy bin\README build\custom-install-standalone\bin copy bin\README build\custom-install-standalone\bin
copy custom-install-finalize.3dsx build\custom-install-standalone copy custom-install-finalize.3dsx build\custom-install-standalone
copy title.db.gz build\custom-install-standalone
copy extras\windows-quickstart.txt build\custom-install-standalone copy extras\windows-quickstart.txt build\custom-install-standalone
copy extras\run_with_cmd.bat build\custom-install-standalone
copy LICENSE.md build\custom-install-standalone copy LICENSE.md build\custom-install-standalone
python -m zipfile -c dist\custom-install-standalone.zip build\custom-install-standalone python -m zipfile -c dist\custom-install-standalone.zip build\custom-install-standalone

74
package.nix Normal file
View File

@@ -0,0 +1,74 @@
{
lib,
pkgs,
python,
callPackage,
buildPythonApplication,
fetchPypi,
pyctr,
pycryptodomex,
pypng,
tkinter,
setuptools,
events,
stdenv,
save3ds,
withGUI ? true,
}:
let
save3ds_no_fuse = save3ds.override { withFUSE = false; };
in
buildPythonApplication rec {
pname = "custominstall";
version = "2.1";
pyproject = true;
src = builtins.path {
path = ./.;
name = "custominstall";
filter =
path: type:
!(builtins.elem (baseNameOf path) [
"build"
"dist"
"localtest"
"__pycache__"
"v"
".git"
"_build"
"custominstall.egg-info"
]);
};
doCheck = false;
build-system = [ setuptools ];
propagatedBuildInputs =
[
pyctr
pycryptodomex
setuptools
events
]
++ lib.optionals (withGUI) [
tkinter
];
makeWrapperArgs = [ "--set CUSTOM_INSTALL_SAVE3DS_PATH ${save3ds_no_fuse}/bin/save3ds_fuse" ];
preFixup = ''
rm -r $out/lib/${python.libPrefix}/site-packages/custominstall/bin
${lib.optionalString (!withGUI) "rm $out/bin/custominstall-gui"}
'';
meta = with lib; {
description = "Installs a title directly to an SD card for the Nintendo 3DS";
homepage = "https://github.com/ihaveamac/custom-install";
license = licenses.mit;
platforms = platforms.unix;
mainProgram = "custominstall";
};
}

48
pyproject.toml Normal file
View File

@@ -0,0 +1,48 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "custominstall"
description = "Installs a title directly to an SD card for the Nintendo 3DS"
authors = [
{ name = "Ian Burgwin", email = "ian@ianburgwin.net" },
]
readme = "README.md"
license = {text = "MIT"}
dynamic = ["version"]
requires-python = ">= 3.8"
classifiers = [
"Topic :: Utilities",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"pyctr>=0.7.6,<0.9",
"setuptools>=61.0.0",
"events>=0.4",
"comtypes>=1.4.12; os_name == 'nt'",
]
[project.gui-scripts]
custominstall-gui = "custominstall.gui:main"
[project.scripts]
custominstall = "custominstall.__main__:main"
[tool.setuptools.dynamic]
version = {attr = "custominstall.__version__"}
[tool.setuptools.packages]
find = {namespaces = false}
# is it even possible to make these OS-specific with pyproject.toml?
[tool.setuptools.package-data]
custominstall = ["bin/darwin/save3ds_fuse", "bin/win32/save3ds_fuse.exe"]

View File

@@ -1,2 +0,0 @@
-r requirements.txt
comtypes==1.1.7

View File

@@ -1,2 +0,0 @@
events==0.4
pyctr==0.4.5

19
setup-cxfreeze.py Normal file
View File

@@ -0,0 +1,19 @@
import sys
from cx_Freeze import setup, Executable
if sys.platform == 'win32':
executables = [
Executable('ci-gui.py', target_name='ci-gui-console'),
Executable('ci-gui.py', target_name='ci-gui', base='Win32GUI'),
]
else:
executables = [
Executable('ci-gui.py', target_name='ci-gui'),
]
setup(
name = "ci-gui",
version = "2.1b4",
description = "Installs a title directly to an SD card for the Nintendo 3DS",
executables = executables
)