QTmS`^(o0fX?-28JGqDT~K
z#w_w+P*b%q(ZJ=xsx1h<}KDX6)*(SN@~0e}RN06$Ld0
zfC?7d-R2%R5Ruf$zEb`krWr=(!)bc@!u=%c4g1LWMU)7UBH)kjTao)&;o+^wDgv4k
z>RifnvzjzPpMv*Nzj`?mFec`r6awOhQ)%T^XhpKt*%Wwt4|hSO(Ivn@AG9
zgIJpcZNvWzYB{ZUvf9`<=uFZhUC$NdGR@(=hDPSOO>!^7C!QY6RV*ou
z=NA>F^X`qNha~}yX!9zXfc?B#m7ZUznWVeBJ0T$f>yrzo##n1Ye0)3)zsv6T%#nOU
z6#->m9P<-4rP}fsEZkFdvFh~59T^%kkM5Eq+W}D*vt2>$FfJ^L7Wb1#FrVM^k8IME
zSwmDp4ojeU0`ScMZN*}vT~rI7+tgC_;QQ5NRTOnUX!tg*qjXX#^OK8Gdt(e%5=pv~
zYFnSbZ+BE#0gaG75bI+8Wjxvz!$v5&`C)EA4SO1~;?#VQ0}A&U9efnF^XDpMyWXJK
zNNtaOQhFlQi?JZva7M|pN^2_uv-
zeg=#S;dVevc4QC`Xajn;;+@7Ndll7E9Vm08Xn>rS^JhcjKoCsuGCdFs|1IkeV
zQY3b((?sF1bQt#``K;7gj6dESDLnxtw;X=h4aMeqe^N_J3jj{K?OvR(n@q~0VsDz?
zP%9U}B>dZ)Y-v1Bl;x`cxfw~KS9#T3!lhvWEEWAFlBucb`qk-5t3$`a7&1{W73Wb2
z7o{Os3@>I5^W$oN^-xMXpW{KFAwY{nZ!N5T8X5!sBNThu5U{_Sj9wksB8}VIrYs`a
zHd3jwK{w|;=sKLit=A*$<3HFmr@(-TSJS3@&g8JdQU=-?lhJGl1gQuSWC*}FpnXFl
z0|5vOR(A@EMyB`gU;BU6u%4x=Y$=r)0-j$%Ye3^>Wc0T&6(hh+3<-PsMFZ1e{jFth
z<;X$za!b=)*yHMu2_xOeA!9!K6BMp`1Q^oVQ2Q7Z$JT9)e=PUdJH!0#
z8P%Rwy8sbrnPqT5#f`>pBb_mu#V{)QRTNy!-|Bu+s~%=<4*;!$IYz@-$L?T6DFTpB
zQ(9m#2K^|6vuCd>=wte73N-^mWRRuj&_vSzJqIa>V|$e?tf-eJV4MiI(T>QIXoKMJ
z#Q0d|j3?XW&}Fq#8f@knPSfi})M>}4fq}Itwm6R#5@=+-E~^>TO1B|oKzhR;0Bj2b
z95->DMeV#S2ceCqk2qN@Iorq`bUU0mm@UH_1Bmi`;%o0`oU({tRl5F93DA$)s*_u+
zC#X`V{ngqH);o>CB>e7DK{$ALdwmf>?JgCJ4yft?u(*JQ@AGPuil+qP!yZ)~z{}^J
z0CtoKczL`Ld(`t-2^n`rK;cWZDCnx?Dgx3;%4)5n-Ldy4hC$npi!4EJ)1D2(DZ?Fl
zzzm(iFp&6N(VGkfj^&KV2_0$HiKxtZl?zp-)Exy@GS#b@PEyKSR>ye$->c^f9~Lj%ymlx!3$$(QZQ1nqE9m+Q4Q
zVl7MOOD9afxS^z6SLb(m9oO`qd-jB)($mvR1^o8;E*+FyOV%A_sMw7qHw_&WJTDx2N?eM@#~6m)bb*dk4}Qg^{lfNq(9
zNsB-7t4gU_S)o<6A~HzeB6zyvgEPWlMHr17#rJ&W^$cE@k+_MJu7qC*%*i(;mfyce
zFKJ6L|G8PYC{zg(x{0buNCGEfsn7hN>St)Z!CAT*4iZz%bJ?WD)LMj+a~vnyju=)C
z+4~3eoFI9AtuO*Q@cYFUH=74e4h~nKrIq}mfVdML+rW;;qVIEex^muq-qlbESo7_(
z#fGx9uVzEo`Q)1HDu2NwWmeVao`444+h*c(0s^i8FZ)ao@VCD|{b5HKKN|dOJ#3hh
zkq5Q_tKYz*lSYfHZcF=b;=TV{;{ERQ4O*1ZA<7{25xcu&|JYg1by+IyNxyV?E#_AckCqe_(t@M@BeK;=cr`m21|#
zJl*y1ZLvez__}L>d`F
zB0L7Qp7Z1?)2drQA*Kmoync-wyzJ}&Yu!CABP+WEbyx#BnlglpG__-&!}d@5jdP{id~_TxWdT(!UmWO_uq*7hK4A#~e>JRIMl@uSwaf4s2u4
z<+7D3)|yJ++s`?X;nV-1Yxa~Npy}=F8=Uub6>eQO*^Q7BNt|@tCnF*%oA%jcG2$*c
z6W#jp%8#iw8D`jQ=_Vv1wc=c6-|iTW=3n?X2=vR@A@%p)YM$3uQG00C=d6IUpR5y$
zh#TsE3~x?Xp7^XK>7sTW
zgUu8|R^mXtDgXefm%wpuyItP5G7YJ=|78%9$Oi!mNDCZCPdH6_@J
z5uHz#lCyzBMWon9C#+XDz^VUhT$lYdIw@J2an{!AEd21ox+ZS%F%rUVwl#|)()D-x
z&y0p3v+w%~!uqkhT|E42Zrl+KU5_vIl_?td8;zl`9|cM>_f~XJiNl20Q!I*?j$^en
z5O9*ybS?b2KiBH)xrByBG?$dp4Bhws5R~ExEz@4Lu_eFlz9VG}0qGZlK2SaqA@4HI
z;d$s9=+H+y>huWb>()Pp))7+cV@BOY?vkL?&6=e0eIzl!#FvQ*?L2uZF*)IRjAi?-
zwzPU^e^dF9$!dTo5Y%Uf`aUU6anhmwi6DKb35$|{UJEmvtQC!G=zv3b
zWp%fFD`tmwD%y;+4KM4pBc}(IcTL9MH`Ryc93{o0slteYwj+LHovw<{h7^JHlQ(`&
zHd$>k=1uM`5snT#BQvx8@Au6tSx2eJfe_p;+Seyb+!PdDoPREA6|!dy2WP7QAY^P}
z!tZg)B+IC$?dxOfkqF8j@?N-OWo_iV-pa)s560o#Y!$;>Y4bk5I#)0qZ~YcyG;hqZ
z`7WxXE1@it*sWZY_HIn4)34o1a)M$f;RgW$fz4d`;Ls4C&poSc4E?EZHfX-j;%^L*
z1&Q2JorQv`YSPGY|7YLTWW0`BL8jv+!bgwEaF$4HHuDr(IRKqJY%w2@(UCPI(T~F|
za&qH}&kH)P3tL+=EMsh(v*a5(JJ*iDtcg@UQv26WO8jEhi^RU?6g9~PX#|C!E&LXF
zpQK)AF841aFU5}h*nVnH#WVYf6!+E+^`*(O7wx|Dme8K5>NxCZd!HxQDTJa5!)f&IlCJU3vc#`1W2j}S{+!}OPITMuKr!O8zV
zGMooj8}6TS)e4%>?>LO;L}!l=P%Hd=^48l_eBN=on8G1~(HJ<)i$Me1U>ra$xboN|
zmJ7S4A2<1)uA2$Vk#H(22LnK{Nqtdxt+5U}#zj$%LuFX5!?;3!VJio^h`|*~YG4hdWn>cAqLw3&
zoeFeBGJPL`y^G5vR(^h>hT(BP+o?SX&rDx*u
zmE`xJox9Jz_Q~@Lch%JnHmT`R-=ttgrwu9SqQKxrkb2Ka2e3mopkA(tX~E9U4&Ybm
zGATz#)<nBaCi@)4*BfY{uaS|
zR0Rcv@qqy;85wH;3>q67YikomCX=l0{lAX%`Zo}$PpsU#iR~Uh8!|_SSfd~#6WRP}
zZL=?Gr;Rq^&iBza#^|9&I$L4K_taan7|MBlmW!#}6i^n(Y@~?JNT;>7YneYFc?)q5h|L1p&grmL+5i!q1Rdmhk0JrJDNK43zSBe@2{vT#foe}^5
literal 0
HcmV?d00001
diff --git a/finalize.lua b/finalize.lua
index ba5632a..da1de34 100644
--- a/finalize.lua
+++ b/finalize.lua
@@ -8,7 +8,24 @@ local lastModified = "2025-07-30"
local json = require('json')
local finalizeRomfs = "0:/finalize.romfs"
-local langCode = "en_US" -- translation support will be added later(TM)
+local langCodes = {
+ English="en_US"
+}
+
+ui.show_png("V:/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]
+
local langPath = CURRDIR .. "/lang/" .. langCode .. ".json"
local lang = json.decode(fs.read_file(langPath, 0, fs.stat(langPath).size))
From 8c803e6695b09d9573e715805138db51cbfccc3b Mon Sep 17 00:00:00 2001
From: ManiacOfGitHub <43019219+ManiacOfGitHub@users.noreply.github.com>
Date: Thu, 11 Sep 2025 22:21:42 -0400
Subject: [PATCH 5/6] new util module, dual locale system, essential check
---
Makefile | 2 +
data/luapackages/finalizeUtil.lua | 11 +++++
finalize.lua | 78 +++++++++++++++++++++++++------
romfs/finalize/lang/en_US.json | 13 ++++++
4 files changed, 90 insertions(+), 14 deletions(-)
create mode 100644 data/luapackages/finalizeUtil.lua
create mode 100644 romfs/finalize/lang/en_US.json
diff --git a/Makefile b/Makefile
index 0f1174a..76b11f2 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ builds/x_finalize_helper.firm: builds/finalize.romfs
@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 $@
@@ -23,3 +24,4 @@ clean:
@rm GodMode9/data/finalize-romfs-hash
@rm GodMode9/data/language_select.png
@rm -rf GodMode9/data/lang
+ @rm -rf GodMode9/data/luapackages/finalizeUtil.lua
diff --git a/data/luapackages/finalizeUtil.lua b/data/luapackages/finalizeUtil.lua
new file mode 100644
index 0000000..bc52f41
--- /dev/null
+++ b/data/luapackages/finalizeUtil.lua
@@ -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
\ No newline at end of file
diff --git a/finalize.lua b/finalize.lua
index da1de34..01ddbc9 100644
--- a/finalize.lua
+++ b/finalize.lua
@@ -6,13 +6,14 @@ 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("V:/language_select.png")
+ui.show_png(CURRDIR .. "/language_select.png")
local languageNames = {}
for i, v in pairs(langCodes) do
@@ -26,8 +27,9 @@ end
local languageSel = languageNames[userSelection]
local langCode = langCodes[languageSel]
-local langPath = CURRDIR .. "/lang/" .. langCode .. ".json"
-local lang = json.decode(fs.read_file(langPath, 0, fs.stat(langPath).size))
+-- 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"])
@@ -36,17 +38,6 @@ if not fs.sd_is_mounted() then
sys.power_off()
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
- ui.echo(string.format(lang["ERROR_04"], ui.format_bytes(minBytes), ui.format_bytes(bytesFree)))
-end
-
local write = "0:/WRITE"
pcall(fs.remove, write)
local success = pcall(fs.make_dummy_file, write, 0x400)
@@ -104,5 +95,64 @@ if gotHash ~= expectedHash then
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()
\ No newline at end of file
diff --git a/romfs/finalize/lang/en_US.json b/romfs/finalize/lang/en_US.json
new file mode 100644
index 0000000..4507c39
--- /dev/null
+++ b/romfs/finalize/lang/en_US.json
@@ -0,0 +1,13 @@
+{
+ "INIT_MESSAGE": "The Finalizing Setup Script is now starting...",
+ "ASK_FOR_HELP": "Ask for help on Discord:\nhttps://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_02": "Error #02: Missing essential.exefs\n \nessential.exefs does not exist.\nTry again,\nmaking sure to say Yes to the\n'Create essential files' popup.",
+ "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 on the next few prompts.",
+ "ERROR_26": "Error #26: SD card not mounted.\n \nThis should not be possible.",
+ "ERROR_30": "Fatal Error #30: Missing console-unique files\n \nThe following files are missing from the NAND:\n%s"
+}
\ No newline at end of file
From 25d245f9365a0fa2539f138dfd36b39cfd6c6911 Mon Sep 17 00:00:00 2001
From: ManiacOfGitHub <43019219+ManiacOfGitHub@users.noreply.github.com>
Date: Thu, 23 Oct 2025 20:11:25 -0400
Subject: [PATCH 6/6] Update GodMode9
---
GodMode9 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/GodMode9 b/GodMode9
index cf1cb1c..2c11812 160000
--- a/GodMode9
+++ b/GodMode9
@@ -1 +1 @@
-Subproject commit cf1cb1cfc5965cec3dd10ba98269aeb37b9b7a9d
+Subproject commit 2c11812cf9e72428d7e5a08f93921481b568fe45