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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GodMode9
Submodule GodMode9 updated 183 files
15 changes: 11 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ builds/finalize.romfs: builds
@3dstool -c -t romfs --romfs-dir romfs --file $@

builds/x_finalize_helper.firm: builds/finalize.romfs
@cp finalize_helper.gm9 GodMode9/data/autorun.gm9
@sed -i s/FINALIZE_SHA256SUM/$(shell sha256sum $< | awk '{print $$1}')/g GodMode9/data/autorun.gm9
@$(MAKE) -C GodMode9 SCRIPT_RUNNER=1
@cp finalize.lua GodMode9/data/autorun.lua
@cp data/language_select.png GodMode9/data/
@cp -r data/lang GodMode9/data/
@cp -r data/luapackages GodMode9/data/
@sha256sum $< | awk '{print $$1}' > GodMode9/data/finalize-romfs-hash
@$(MAKE) -C GodMode9 SCRIPT_RUNNER=1 AUTO_UNLOCK=1
@cp GodMode9/output/GodMode9.firm $@
@printf '\001' | dd conv=notrunc bs=1 seek=16 of=$@
clean:
@rm -rf builds
@$(MAKE) -C GodMode9 clean
@rm GodMode9/data/autorun.gm9
@rm GodMode9/data/autorun.lua
@rm GodMode9/data/finalize-romfs-hash
@rm GodMode9/data/language_select.png
@rm -rf GodMode9/data/lang
@rm -rf GodMode9/data/luapackages/finalizeUtil.lua
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
Scripts relating to Finalizing Setup on https://3ds.hacks.guide/finalizing-setup.

- [`/romfs/finalize/`](romfs/finalize): Files that are packed into `finalize.romfs`
- [`/romfs/finalize/img`](romfs/finalize/img): Images used for visual troubleshooting
- [`/romfs/finalize/finalize.gm9`](romfs/finalize/finalize.gm9): Script run after `finalize_helper.gm9` that:
- Installs base homebrew applications to SYSNAND SD (see below for list)
- Copies GodMode9 to CTRNAND (`/rw/luma/payloads`)
- Backs up `essential.exefs` to `/gm9/backups`
- Deletes CFW installation files that are no longer necessary
- Backs up minsize NAND backup to `/gm9/backups`
- `/romfs/finalize/donor.db`: Empty title database used for consoles that do not have title database (i.e. no eShop software)
- [`finalize_helper.gm9`](finalize_helper.gm9): Script that is compiled as GM9 scriptrunner (`finalize_helper.firm`); extracts `finalize.romfs`
- [`/romfs/finalize/img`](romfs/finalize/img): Images used for visual troubleshooting
- [`/romfs/finalize/donor.db`](romfs/finalize/donor.db): Empty title database used for consoles that do not have title database (i.e. no eShop software)
- [`finalize_helper.lua`](finalize_helper.lua): Lua script that is compiled as GM9 scriptrunner (`x_finalize_helper.firm`) that:
- Installs base homebrew applications to SYSNAND SD (see below for list)
- Copies all payloads inside `/luma/payloads` on the SD card to CTRNAND (`/rw/luma/payloads`)
- Backs up `essential.exefs` to `/gm9/backups`
- Deletes CFW installation files that are no longer necessary
- Backs up minsize NAND backup to `/gm9/backups`
- [`docs.md`](docs.md): Full error information / script documentation

## Bundled software
Expand All @@ -32,7 +31,7 @@ Releases are tagged for reference (based on usage in the guide). **Releases in t

### Automatically built binaries

Binaries are automatically built by [GitHub Actions](https://github.com/hacks-guide/finalize/actions/). Place `finalize_helper.firm` in `/luma/payloads/` and `finalize.romfs` on root of SD.
Binaries are automatically built by [GitHub Actions](https://github.com/hacks-guide/finalize/actions/). Place `x_finalize_helper.firm` in `/luma/payloads/` and `finalize.romfs` on root of SD.

### Manual file placement

Expand Down
11 changes: 11 additions & 0 deletions data/lang/en_US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"INIT_MESSAGE": "The Finalizing Setup Script is now starting...",
"ASK_FOR_HELP": "If this error persists, ask for help\non Discord: https://discord.gg/MWxPgEp",

"ERROR_00": "Error #00: Build error\nYou have encountered an error\nthat should only occur if the script\nwas built incorrectly.",
"ERROR_04": "Error #04: No space\n \nInsufficient space on SD card.\nYou need %s, but you have %s.\nMake some space, then try again.\n \nTIP: You can temporarily remove the Nintendo 3DS\nand DCIM folders from your SD card\nto make enough space.",
"ERROR_21": "Error #21: finalize.romfs not found\n \nfinalize.romfs could not be found on the SD card.\nCopy it to root of SD and try again.",
"ERROR_22": "Error #22: finalize.romfs is invalid\n \nThe file finalize.romfs is corrupt or unreadable.\nRe-download it, copy it to root of SD, and try again.\n \nExpected: %s\n \nReceived: %s",
"INFO_23" : "Information #23: finalize.romfs in wrong location\n \nfinalize.romfs is in the wrong location.\nThis script will attempt to move it.\nPlease hit <A> on the next few prompts.",
"ERROR_26": "Error #26: SD card not mounted.\n \nThis should not be possible."
}
Binary file added data/language_select.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions data/luapackages/finalizeUtil.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local finalizeUtil = {}

function finalizeUtil.error(text, image, powerOff)
ui.show_png("9:/finalize/img/" .. image .. ".png")
ui.echo(text)
if powerOff then
sys.power_off()
end
end

return finalizeUtil
158 changes: 158 additions & 0 deletions finalize.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
--[[
Script for https://3ds.hacks.guide/finalizing-setup
This script is not intended be run manually.
Credits have been moved to within the script's optional menu.
--]]
local scriptVersion = "2.0.0"
local lastModified = "2025-07-30"
local json = require('json')
local finalizeUtil = require('finalizeUtil')
local finalizeRomfs = "0:/finalize.romfs"

local langCodes = {
English="en_US"
}

ui.show_png(CURRDIR .. "/language_select.png")

local languageNames = {}
for i, v in pairs(langCodes) do
table.insert(languageNames, i)
end

local userSelection = ui.ask_selection("", languageNames)
if not userSelection then
sys.power_off()
end
local languageSel = languageNames[userSelection]
local langCode = langCodes[languageSel]

-- Load temporary locale file, complete one is stored in finalize.romfs because all of the locales don't fit in VRAM
local tempLangPath = CURRDIR .. "/lang/" .. langCode .. ".json"
local lang = json.decode(fs.read_file(tempLangPath, 0, fs.stat(tempLangPath).size))

ui.show_text(lang["INIT_MESSAGE"])

if not fs.sd_is_mounted() then
ui.echo(string.format("%s\n \n%s", lang["ERROR_26"], lang["ASK_FOR_HELP"]))
sys.power_off()
end

local write = "0:/WRITE"
pcall(fs.remove, write)
local success = pcall(fs.make_dummy_file, write, 0x400)
if not success then
ui.echo(string.format("%s\n \n%s", lang["ERROR_25"], lang["ASK_FOR_HELP"]))
sys.power_off()
end
pcall(fs.remove, write)

local romfsCheckDirectoryList = {"0:", "0:/3ds", "0:/luma/payloads", "0:/luma", "0:/DCIM"}
local filenameMatchList = {"finalize.romfs", "finalize(*).romfs", "finalize (*).romfs"}
for _,dir in ipairs(romfsCheckDirectoryList) do
for _,filename in ipairs(filenameMatchList) do
local success, filesFound = pcall(fs.find_all, dir, filename)
if success then
for _,path in ipairs(filesFound) do
if path ~= "0:/finalize.romfs" then
pcall(fs.remove, "0:/finalize.romfs")
pcall(fs.move, path, finalizeRomfs, {no_cancel = true, silent = true, overwrite = true})
end
end
end
end
end
for _,filename in ipairs(filenameMatchList) do
local success, filesFound = pcall(fs.find_all, "0:/Nintendo 3DS/", filename)
if success then
for _,path in ipairs(filesFound) do
ui.echo(lang["INFO_23"])
pcall(fs.move, "0:/Nintendo 3DS/" .. path, finalizeRomfs, {no_cancel = true, silent = true, overwrite = true})
end
end
end

if not fs.exists("0:/finalize.romfs") then
ui.echo(lang["ERROR_21"])
sys.power_off()
end

local success, expectedHash = pcall(fs.read_file, CURRDIR.."/finalize-romfs-hash", 0, 64)
if not success then
ui.echo(string.format("%s\n \n%s\nfinalize romfs hash file does not exist", lang["ERROR_00"], lang["ASK_FOR_HELP"]))
sys.power_off()
end

local success, result = pcall(fs.hash_file, finalizeRomfs, 0, 0)
if not success then
ui.echo(string.format(lang["ERROR_22"], expectedHash, "hash failed"))
sys.power_off()
end
local gotHash = util.bytes_to_hex(result)

if gotHash ~= expectedHash then
ui.echo(string.format(lang["ERROR_22"], expectedHash, gotHash))
sys.power_off()
end

local success, result = pcall(fs.img_mount, finalizeRomfs)
if not success then
ui.echo(string.format(lang["ERROR_22"], expectedHash, gotHash) .. "\nImage mount failed... somehow?")
sys.power_off()
end

local success, result = pcall(fs.copy, "G:/finalize", "9:/finalize", {overwrite=true, recursive=true, silent=true})
if not success then
ui.echo(string.format(lang["ERROR_22"], expectedHash, gotHash) .. "\nCopying to RAM failed... somehow?")
sys.power_off()
end

-- We're done with finalize.romfs now
fs.img_umount()


-- New locale files can now be mounted, some finalizeUtil functions become usable too.
local newLangPath = "9:/finalize/lang/" .. langCode .. ".json"
lang = json.decode(fs.read_file(newLangPath, 0, fs.stat(newLangPath).size))

-- Check for missing essentials
-- BuildEssentialBackup() will return 1 (failure) if any of these files are missing. As well as nand_hdr.bin, but like lol
local missingEssential = ""

if not (fs.find("1:/rw/sys/SecureInfo_A") or fs.find("1:/rw/sys/SecureInfo_B")) then
missingEssential = missingEssential .. "SecureInfo\n"
end

if not (fs.find("1:/rw/sys/LocalFriendCodeSeed_B") or fs.find("1:/rw/sys/LocalFriendCodeSeed_A")) then
missingEssential = missingEssential .. "LocalFriendCodeSeed\n"
end

if not fs.find("1:/private/movable.sed") then
missingEssential = missingEssential .. "movable.sed\n"
end

-- Check for essential.exefs, create if doesn't exist

local success = sys.check_embedded_backup()
if (not success) or (not fs.find("S:/essential.exefs")) then
if missingEssential ~= "" then
finalizeUtil.error(string.format(lang["ERROR_30"], missingEssential) .. "\n \n" .. lang["ASK_FOR_HELP"], "error30", true)
else
finalizeUtil.error(lang["ERROR_02"], "error02", true)
end
end

local minBytes
if CONSOLE_TYPE == "O3DS" then
minBytes = (1024 ^ 3)
else
minBytes = (1024 ^ 3) * 1.4
end
local bytesFree = fs.stat_fs("0:/").free
if bytesFree < minBytes then
finalizeUtil.error(string.format(lang["ERROR_04"], ui.format_bytes(minBytes), ui.format_bytes(bytesFree)), "error04", true)
end


ui.echo("The script finished without errors.\n(This script is still in development)")
sys.power_off()
97 changes: 0 additions & 97 deletions finalize_helper.gm9

This file was deleted.

Loading