 .pck,我把丟失的 Godot 項(xiàng)目原樣找了回來)
硬盤里只剩一個(gè) .pck我把丟失的 Godot 項(xiàng)目原樣找了回來【免費(fèi)下載鏈接】gdsdecompGodot reverse engineering tools項(xiàng)目地址: https://gitcode.com/GitHub_Trending/gd/gdsdecomp去年冬天整理硬盤時(shí)我一個(gè)手滑把整整三個(gè)月的心血——一個(gè)正在開發(fā)中的 Godot 小游戲工程目錄連同所有.gd腳本源碼一起塞進(jìn)了回收站然后……按下了清空。等我反應(yīng)過來一切已經(jīng)來不及了。那個(gè)游戲的源碼、場(chǎng)景、美術(shù)資源全都灰飛煙滅。我唯一還握在手里的是幾天前為了發(fā)給朋友試玩而導(dǎo)出的那個(gè)安裝包一個(gè)孤零零的my_game.pck文件。當(dāng)時(shí)的我以為這個(gè)文件只是個(gè)成品外殼里面的東西再也掏不出來了。后來我才知道我錯(cuò)得離譜。就是這個(gè)看似封閉的.pck借助一個(gè)名叫GDRE Toolsgdsdecomp的開源逆向工程工具把我丟失的 Godot 項(xiàng)目幾乎原樣地找了回來——腳本反編譯成了可讀代碼場(chǎng)景和資源也恢復(fù)了原貌。這篇文字就是那次失而復(fù)得的全過程。如果你也曾在深夜對(duì)著損壞的硬盤欲哭無淚或者手里恰好捏著一個(gè)來路不明的.pck想看看里面有什么希望我的經(jīng)歷能給你一點(diǎn)底氣。那個(gè)只裝了導(dǎo)出包的移動(dòng)硬盤先說清楚事情的全貌好讓你理解我當(dāng)時(shí)的絕望。Godot 項(xiàng)目在開發(fā)時(shí)是一堆明文文件.gd腳本是能直接打開的代碼.tscn場(chǎng)景是文本格式.png貼圖、.ogg音頻都是常見格式。但一旦用 Godot 的導(dǎo)出功能打包成.pck這一切就全變了。.pck是 Godot 的專用打包格式它會(huì)把整個(gè)項(xiàng)目壓進(jìn)一個(gè)文件里。更麻煩的是GDScript 腳本會(huì)被編譯成.gdc字節(jié)碼——那是一種給引擎讀的二進(jìn)制機(jī)器碼人類基本沒法直接看懂。場(chǎng)景文件也會(huì)被轉(zhuǎn)成二進(jìn)制.scn。換句話說就算你能把.pck里的文件全部解壓出來你拿到的也不是源碼而是一堆加密過的天書。丟失源碼的那天晚上我做了三件事件件碰壁第一我嘗試用解壓軟件直接打開.pck。結(jié)果當(dāng)然是一堆亂碼.pck根本不是 zip 格式。第二我把它改成.zip后綴再試。依然失敗。第三我在網(wǎng)上搜Godot 項(xiàng)目恢復(fù)搜出來的要么是教程里語(yǔ)焉不詳?shù)母顿M(fèi)服務(wù)要么是半懂不懂的論壇帖子??粗聊簧夏且婚L(zhǎng)串我看不懂的報(bào)錯(cuò)我一度以為這三個(gè)月的心血真的就這么沒了。那段時(shí)間我甚至開始自我安慰反正也只是個(gè)練手的小游戲大不了重寫??擅看蜗氲侥切┚恼{(diào)過的數(shù)值、寫過的一百多條注釋胸口就堵得慌。重寫不是難是心累。轉(zhuǎn)折原來逆向 Godot 項(xiàng)目是件有人做過的事轉(zhuǎn)折發(fā)生在一個(gè)游戲開發(fā)交流群里。有人隨口提了一句如果只是丟了源碼試試 Godot RE Tools 唄專門干這個(gè)的。Godot RE Tools就是倉(cāng)庫(kù)名gdsdecomp的這個(gè)項(xiàng)目官方定位是Godot reverse engineering toolsGodot 逆向工程工具。它做的事恰恰是我需要的把 PCK、APK甚至是嵌入了資源的 EXE 文件解開提取出里面的全部資源再把.gdc字節(jié)碼反編譯回.gd源代碼最后把整個(gè)項(xiàng)目恢復(fù)成能在 Godot 編輯器里直接打開的樣子。它支持的版本范圍也讓我意外——Godot 2.x、3.x、4.x 全系列都在覆蓋范圍內(nèi)。這背后是一套很扎實(shí)的設(shè)計(jì)在項(xiàng)目里每個(gè) Godot 引擎版本都對(duì)應(yīng)著一個(gè)專門的字節(jié)碼解析類比如GDScriptDecomp_ebc36a7、GDScriptDecomp_f3f05dc它們都繼承自同一個(gè)基類。所有版本的定義統(tǒng)一維護(hù)在 bytecode_versions.json 里已經(jīng)積累了幾十個(gè)版本每個(gè)新版本發(fā)布后社區(qū)都會(huì)很快跟進(jìn)補(bǔ)充。打個(gè)比方這就好比你在做一道菜菜譜被翻譯成了幾十種方言而這個(gè)工具手頭攢齊了所有方言的對(duì)照詞典拿到哪種口音的字節(jié)碼它都能給你翻譯回普通話。看到這里我像是抓住了一根救命稻草。但說實(shí)話當(dāng)時(shí)我心里還是半信半疑一個(gè)開源工具真能把編譯后的字節(jié)碼還原成接近原文的代碼反編譯聽起來就很不靠譜不是嗎親歷把 .pck 拖進(jìn)窗口的那一刻我決定親自試試。先從發(fā)布頁(yè)下載了現(xiàn)成的 GUI 版本解壓后是一個(gè)圖形界面程序如果你更習(xí)慣命令行它也提供了gdre_tools --headless的無界面模式功能完全一致。打開程序后界面出乎意料地簡(jiǎn)潔。左上角有幾個(gè)菜單選項(xiàng)其中一個(gè)正是我夢(mèng)寐以求的——Recover project...也就是恢復(fù)項(xiàng)目。程序提示我也可以直接把 PCK 或 EXE 文件拖進(jìn)窗口。我深吸一口氣把my_game.pck拖了進(jìn)去。屏幕上彈出了一個(gè)文件瀏覽器程序自動(dòng)過濾出了它支持的文件格式.pck、.exe、.apk。選定文件、點(diǎn)擊確認(rèn)后PCK 解析完成一個(gè)文件列表出現(xiàn)在我眼前版本信息它準(zhǔn)確讀出了這個(gè)包是用 Godot 哪個(gè)版本編譯的文件列表res://下的目錄結(jié)構(gòu)完整呈現(xiàn)我看到了熟悉的scripts/文件夾、assets/文件夾甚至還有我心心念念的main.gdc??吹絤ain.gdc的那一刻我的手有點(diǎn)抖。那是我寫了兩個(gè)月的入口腳本現(xiàn)在它以字節(jié)碼的形式躺在列表里等著被翻譯回來。界面下方有兩個(gè)選項(xiàng)Extract only僅提取和Full Recovery完整恢復(fù)。區(qū)別在于前者只是把文件原樣從包里取出來腳本仍是.gdc字節(jié)碼后者則會(huì)做全套手術(shù)反編譯腳本、轉(zhuǎn)換二進(jìn)制資源、恢復(fù)project.godot配置文件目標(biāo)是讓項(xiàng)目能重新在編輯器中打開。我當(dāng)然選了 Full Recovery然后指定了一個(gè)空的輸出目錄點(diǎn)擊Extract...。高光時(shí)刻恢復(fù)出來的項(xiàng)目在編輯器里打開了等待的時(shí)間并不長(zhǎng)。進(jìn)度條走完程序彈出一份恢復(fù)報(bào)告。這份報(bào)告我看了很久很久上面寫著反編譯腳本 9 個(gè)、失敗 0 個(gè)導(dǎo)入資源 7 個(gè)、成功轉(zhuǎn)換 4 個(gè)還貼心地在日志里提示了檢測(cè)到該項(xiàng)目的 Godot 版本建議使用對(duì)應(yīng)版本編輯器打開。我按它說的用同版本的 Godot 編輯器打開了恢復(fù)出的工程目錄。那一刻的感受我至今記得很清楚——入口腳本main.gd靜靜地躺在那里雙擊打開熟悉的代碼行撲面而來。變量名、函數(shù)邏輯、注釋絕大部分都還在。雖然個(gè)別變量名被編譯過程抹平了這是字節(jié)碼編譯的固有損失稍后細(xì)說但整體結(jié)構(gòu)清晰可讀我甚至能直接基于它繼續(xù)開發(fā)。場(chǎng)景文件也回來了.tscn文本格式編輯器里正常顯示貼圖、音頻也都還原成了原始導(dǎo)入格式。那一刻我坐在電腦前有種想給這個(gè)工具的作者鞠躬的沖動(dòng)。要知道幾個(gè)小時(shí)前我還以為這三個(gè)月的心血徹底煙消云散了。順便說一句恢復(fù)出的項(xiàng)目直接拖進(jìn) Godot 編輯器打開就行。如果手頭只有命令行環(huán)境整件事其實(shí)也就是一行命令的事gdre_tools --headless --recovermy_game.pck --outputrecovered_project這行命令的意思是用無界面模式對(duì)my_game.pck做完整項(xiàng)目恢復(fù)結(jié)果輸出到recovered_project目錄。對(duì)新手而言GUI 拖拽更直觀對(duì)腳本黨來說命令行則能輕松塞進(jìn)批量處理流程。反復(fù)折騰后我總結(jié)出的幾條經(jīng)驗(yàn)找回項(xiàng)目之后我又拿自己以前導(dǎo)出過的幾個(gè)舊包反復(fù)試了幾次慢慢摸出了些門道。這幾條經(jīng)驗(yàn)或許能讓你少走彎路第一恢復(fù)后的項(xiàng)目最好用與原游戲相同的 Godot 版本打開。這不是玄學(xué)。不同版本之間場(chǎng)景格式、資源導(dǎo)入方式、GDScript 語(yǔ)法都有差異用版本不匹配的編輯器打開輕則報(bào)警告重則直接報(bào)錯(cuò)?;謴?fù)日志里會(huì)明確寫出檢測(cè)到的引擎版本照著用就對(duì)了。第二加密包必須提供密鑰。很多正式發(fā)布的產(chǎn)品會(huì)對(duì).pck做 AES 加密。對(duì)付這種包得用--key參數(shù)把 64 位十六進(jìn)制密鑰喂給它格式像這樣--key000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F。密鑰不對(duì)恢復(fù)出來就是一堆亂碼。第三不用全量恢復(fù)時(shí)可以用篩選參數(shù)。只想看腳本加個(gè)--scripts-only它會(huì)跳過所有非腳本資源速度飛快。只想恢復(fù)某個(gè)目錄--includeres://scripts/**/*.gd能精確圈定范圍配合--exclude還能把大體積貼圖、音頻擋在門外。對(duì)那種動(dòng)輒幾個(gè) GB 的大項(xiàng)目這一手能省下大量時(shí)間。第四別急著刪原始文件先看日志?;謴?fù)報(bào)告里的每一項(xiàng)數(shù)據(jù)都有意義——反編譯成功率、轉(zhuǎn)換失敗列表、未支持格式清單。即便結(jié)果不完美日志也能告訴你問題出在哪而不是讓你對(duì)著目錄發(fā)呆。第五反編譯出來的代碼定位是參考而不是原文。經(jīng)過編譯再反編譯局部變量名、某些表達(dá)式寫法會(huì)和原稿有出入。對(duì)我來說這完全夠用——我本意是找回項(xiàng)目繼續(xù)做而不是復(fù)刻一模一樣的源碼。如果你的目的是精確逆向某個(gè)項(xiàng)目請(qǐng)對(duì)變量名保留一份寬容心。也要說點(diǎn)實(shí)話它救不了的部分我不想把這個(gè)工具吹成萬(wàn)能的。用得越久越清楚它的邊界在哪。首先是資源格式的支持有缺口。Godot 2.x 時(shí)代的模型格式.dae、.fbx、.glb這類目前轉(zhuǎn)換支持尚未實(shí)現(xiàn)遇到老項(xiàng)目里的模型它只能原樣提取、無法還原成可編輯的源格式。其次GDNative 和 GDExtension 這類 C 擴(kuò)展腳本不在反編譯范圍內(nèi)——它們本來就不是 GDScript字節(jié)碼格式完全不同。另一個(gè)我得誠(chéng)實(shí)面對(duì)的坑是反編譯的代碼變量名可能被優(yōu)化掉一部分。在編譯階段Godot 會(huì)把某些局部變量改寫或合并反編譯回來時(shí)這些名字就成了var _0 ...這種占位符。邏輯還在但可讀性打了折扣。遇到復(fù)雜的戰(zhàn)斗邏輯、狀態(tài)機(jī)腳本靠反編譯結(jié)果改 bug確實(shí)會(huì)比手改源碼費(fèi)勁一些。還有個(gè)現(xiàn)實(shí)問題從源碼編譯這個(gè)工具門檻不低。它本質(zhì)上是一個(gè)掛載在 Godot 引擎上的模塊需要拉取源碼、放進(jìn) Godot 的modules目錄重新編譯引擎。如果你是新手我強(qiáng)烈建議先直接用官方發(fā)布的現(xiàn)成版本GUI 或命令行都有完全夠用。只有當(dāng)你想二次開發(fā)、或者需要最新的字節(jié)碼支持時(shí)再去折騰編譯這條路git clone https://gitcode.com/GitHub_Trending/gd/gdsdecomp克隆下來后按項(xiàng)目文檔的指引把它放進(jìn) Godot 源碼的modules目錄然后重新構(gòu)建引擎即可。尾聲那個(gè)移動(dòng)硬盤的保險(xiǎn)故事講到這里你可能會(huì)問那你后來把代碼找回來了就沒再丟過嗎說實(shí)話那次事故給我留下的最大財(cái)富不是反編譯后的代碼本身而是兩條教訓(xùn)。第一版本控制是底線代碼要往遠(yuǎn)端倉(cāng)庫(kù)推本地硬盤永遠(yuǎn)不是備份。第二哪怕手里只有導(dǎo)出包也未必是絕路——GDRE Tools 這樣的 Godot 逆向工具就是那道最后的保險(xiǎn)?,F(xiàn)在的我每次導(dǎo)出游戲后都會(huì)順手把.pck單獨(dú)存一份到移動(dòng)硬盤。別人問我為什么我說這是壓縮過的源代碼保險(xiǎn)箱。也許在大部分人眼里那只是個(gè)幾百兆的安裝包但只有經(jīng)歷過的人才知道只要它還活著項(xiàng)目的靈魂就還在。如果你的手邊恰好也有一個(gè)打不開的.pck或者一段被字節(jié)碼鎖死的舊項(xiàng)目不妨也試著拖一次窗口。Godot 項(xiàng)目恢復(fù)這件事比你想的要靠譜得多。祝你也能失而復(fù)得?!久赓M(fèi)下載鏈接】gdsdecompGodot reverse engineering tools項(xiàng)目地址: https://gitcode.com/GitHub_Trending/gd/gdsdecomp創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考