mirror of
https://github.com/ihaveamac/custom-install.git
synced 2026-01-21 14:06:02 +00:00
initial python packaging and nix flake
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -11,12 +11,15 @@ testing-class.py
|
||||
venv/
|
||||
**/__pycache__/
|
||||
*.pyc
|
||||
*.egg-info/
|
||||
|
||||
# JetBrains
|
||||
.idea/
|
||||
=======
|
||||
|
||||
*.pyc
|
||||
/build/
|
||||
/dist/
|
||||
/custom-install-finalize.3dsx
|
||||
|
||||
result
|
||||
result-*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
10
custominstall/__init__.py
Normal file
10
custominstall/__init__.py
Normal 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'
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# 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).
|
||||
# You can find the full license text in LICENSE.md in the root of this project.
|
||||
|
||||
@@ -10,7 +10,7 @@ from argparse import ArgumentParser
|
||||
from enum import Enum
|
||||
from glob import glob
|
||||
import gzip
|
||||
from os import makedirs, rename, scandir
|
||||
from os import makedirs, rename, scandir, environ
|
||||
from os.path import dirname, join, isdir, isfile
|
||||
from random import randint
|
||||
from hashlib import sha256
|
||||
@@ -36,6 +36,8 @@ from pyctr.type.ncch import NCCHSection
|
||||
from pyctr.type.tmd import TitleMetadataError
|
||||
from pyctr.util import roundup
|
||||
|
||||
from . import __version__
|
||||
|
||||
if platform == 'msys':
|
||||
platform = 'win32'
|
||||
|
||||
@@ -46,15 +48,21 @@ if is_windows:
|
||||
else:
|
||||
from os import statvfs
|
||||
|
||||
CI_VERSION = '2.1'
|
||||
|
||||
# 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'
|
||||
frozen = getattr(sys, 'frozen', False)
|
||||
script_dir: str
|
||||
if frozen:
|
||||
script_dir = dirname(executable)
|
||||
save3ds_fuse_path = join(script_dir, 'bin', save3ds_fuse_name)
|
||||
else:
|
||||
script_dir = dirname(__file__)
|
||||
save3ds_fuse_path = join(script_dir, 'bin', platform, save3ds_fuse_name)
|
||||
|
||||
# missing contents are replaced with 0xFFFFFFFF in the cmd file
|
||||
CMD_MISSING = b'\xff\xff\xff\xff'
|
||||
@@ -281,12 +289,6 @@ class CustomInstall:
|
||||
return isdir(sd_path)
|
||||
|
||||
def start(self):
|
||||
if frozen:
|
||||
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)
|
||||
return None, False, 0
|
||||
@@ -692,7 +694,7 @@ class CustomInstall:
|
||||
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.add_argument('cia', help='CIA files', nargs='+')
|
||||
parser.add_argument('-m', '--movable', help='movable.sed file', required=True)
|
||||
@@ -703,7 +705,7 @@ if __name__ == "__main__":
|
||||
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')
|
||||
|
||||
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()
|
||||
|
||||
installer = CustomInstall(boot9=args.boot9,
|
||||
@@ -751,3 +753,7 @@ if __name__ == "__main__":
|
||||
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()
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# 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).
|
||||
# You can find the full license text in LICENSE.md in the root of this project.
|
||||
|
||||
@@ -25,7 +25,8 @@ from pyctr.type.cdn import CDNError
|
||||
from pyctr.type.cia import CIAError
|
||||
from pyctr.type.tmd import TitleMetadataError
|
||||
|
||||
from custominstall import CustomInstall, CI_VERSION, load_cifinish, InvalidCIFinishError, InstallStatus
|
||||
from . import __version__
|
||||
from .__main__ import CustomInstall, load_cifinish, InvalidCIFinishError, InstallStatus
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from os import PathLike
|
||||
@@ -497,7 +498,7 @@ class CustomInstallGUI(ttk.Frame):
|
||||
self.status_label = ttk.Label(self, text='Waiting...')
|
||||
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:
|
||||
self.log('Note: Could not load taskbar lib.')
|
||||
@@ -735,8 +736,13 @@ class CustomInstallGUI(ttk.Frame):
|
||||
Thread(target=install).start()
|
||||
|
||||
|
||||
def main():
|
||||
window = tk.Tk()
|
||||
window.title(f'custom-install {CI_VERSION}')
|
||||
window.title(f'custom-install {__version__}')
|
||||
frame = CustomInstallGUI(window)
|
||||
frame.pack(fill=tk.BOTH, expand=True)
|
||||
window.mainloop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
34
default.nix
Normal file
34
default.nix
Normal 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;
|
||||
};
|
||||
}
|
||||
82
finalize/flake.lock
generated
Normal file
82
finalize/flake.lock
generated
Normal 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
31
finalize/flake.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
}
|
||||
93
flake.lock
generated
Normal file
93
flake.lock
generated
Normal 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
47
flake.nix
Normal 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}
|
||||
);
|
||||
};
|
||||
}
|
||||
72
package.nix
Normal file
72
package.nix
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
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 = 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";
|
||||
};
|
||||
}
|
||||
44
pyproject.toml
Normal file
44
pyproject.toml
Normal file
@@ -0,0 +1,44 @@
|
||||
[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}
|
||||
@@ -1,2 +0,0 @@
|
||||
-r requirements.txt
|
||||
comtypes==1.1.10
|
||||
@@ -1,2 +0,0 @@
|
||||
events==0.4
|
||||
pyctr>=0.4,<0.7
|
||||
Reference in New Issue
Block a user