總的筆記:https://www.cnblogs.com/guobaoxu/p/12055930.html
目錄
MonoBehavior是開發過程中綁定在物體上的腳本被序列化后得到的,在漢化過程中,修改文字經常會需要修改這種類型的資源,字體的修改過程也會涉及到。
一、使用工具:
Unity版本:2018.4.5f1
AssetStudio(地址:https://www.perfare.net/tag/assetstudio)
UABE(地址:https://7daystodie.com/forums/showthread.php?22675-Unity-Assets-Bundle-Extractor)
il2cppdumper(地址:https://www.perfare.net/tag/il2cppdumper)
二、腳本在序列化文件中的表現形式
正向開發中,類中的public字段會放進檢查器Inspector中(不考慮屬性的控制的話),而在序列化的時候,首先會將類的信息序列化為一個MonoScript,綁定在場景物體上的具體對象則是序列化為MonoBehavior,一個類可以多次實例化,所以一個MonoScript會被多個MonoBehavior引用。
給一個具體的例子,寫一個簡單的類,代碼如下
namespace MyNamespace { public class MyScript : MonoBehaviour { public bool myBool; public string myStr; public int myInt; } }
在檢查器Inspector中會對應出現下面三個參數:

然后我們編譯一下,再用Asset Studio預覽,會找到下面這個

在右側預覽中,第一個指針指向的是這個MonoBehavior所綁定的場景對象,然后是Enabled,也就是檢查器Inspector中名字左側的勾,第二個指針指向的就是描述了類信息的MonoScript,值得注意的是,在MonoScript中,只描述了類的所屬DLL、命名空間、類名等,並沒有字段的信息,然后就是字段區域了,他的具體二進制形式也可以關注一下,用UABE導出一下二進制,內容如下

拆解一下,對應關系如下:
00 00 00 00 02 00 00 00 00 00 00 00 指向GameObject的指針 01 00 00 00 Enabled 01 00 00 00 67 00 00 00 00 00 00 00 指向MonoScript 00 00 00 00 這個MonoBehavior的名字 01 00 00 00 字段myBool,布爾值占4byte 09 00 00 00 E5 AD 97 E7 AC A6 E4 B8 B2 00 00 00 字段myStr,先長度,然后UTF-8編碼,最后向4Byte對齊 39 30 00 00 字段myInt
可以看到在MonoScript的指針之后就是參數區域了,實際上如果把父類和非基本類型一並考慮進來,參數區域的序列化規則如下:
(1)先放父類的變量,再放入一個String(跟類有關系),再放入當前類內的字段;字段按照類內定義的順序放
(2)基本類型會直接放,其中布爾值會用占4byte,可參考上面的例子
(3)String是先一個Int表示實際長度,再放入UTF-8編碼的內容,最后補0,使得最后占據的總長度和4byte對齊
(4)Unity定義的資源會用PPtr來指向
(5)其他類的字段需要先有serializable屬性才會進行序列化,規則類似(這還是個遞歸的過程)
如果想要深入理解,建議自己多做幾個類看看
三、對MonoBehavior的參數進行修改
既然知道了在檢查器Inspector中設置的字段會被序列化進資源文件,那么我們就可以從這里入手給他做修改。先對樣例做點修改,把腳本綁到一個Text上,再在腳本里加一段代碼:
void Start() { Text text = gameObject.GetComponent<Text>(); text.text = myStr; }
這樣運行起來之后效果如下:

接下來是修改的全過程,遇到的問題我盡可能記下來了,所以比較長
(1)尋找和定位
尋找主要利用AssetStudio,直接用AssetStudio加載Data文件夾,菜單欄-Filter Type-MonoBehaviour,篩選只看MonoBehavior類型,如果沒有其他信息,就只能直接一個一個看了,第一次點擊的時候會彈窗“Select Assembly Folder”,這里是要求選擇DLL所在的文件夾。
【問題1】關於類的信息
前面說過了,類的信息會放在MonoScript中,但是MonoScript中只有類名,沒有字段信息,所以要解析參數區域,還需要DLL,這很好理解。
但是涉及到DLL,又出現新的問題,Mono腳本后端的DLL就直接放在assets\bin\Data\Managed文件夾下,而il2cpp腳本后端則是編譯成了so文件,所以針對il2cpp腳本后端,還需要另一個工具的幫助,il2cppdumper,他的具體操作建議看官網,利用il2cppdumper,可以得到DummyDLL,這里面沒有實際的代碼邏輯,但是有字段信息,足夠使用了。
找到我們需要的MonoBehavior之后,右鍵Show original File,可以看到他在level0文件。
然后要用UABE找到具體的這個MonoBehavior,用UABE打開level0,有幾個信息可以幫助我們定位到該MonoBehavior,首先是Type列,里面有完整的類名,而AssetStudio中也會有這個名字(AS左側列表中的Name列,有名字就顯示名字,沒名字就顯示類名);其次是Size列,大小肯定是不會變的。找到該MonoBehavior所在的行如下

(2)修改之文本形式
修改靠的是UABE,因為UABE中有兩種導入導出的方式,二進制RAW和文本形式,所以這里也分兩種來說。
先用UABE導出文本,點擊右側的Export Dump,會彈窗提示:

這跟【問題1】是同一個問題,UABE會自動現在Data/Manager文件夾下找DLL,如果有DLL找不到,在第一次導出文本的時候就會提示這個框,要你給他找那些找不到的DLL。點擊“是”,然后給他找缺少的DLL,然后就會發現一個新的問題,很多DLL根本沒有,這很正常,因為Unity有很多的默認資源,這些資源里引用的類又不一定會編譯進來,所以一般來說在Manager文件夾下有的、或者il2cppdumper導出來的給他選上就夠了。導出來的文本如下:
0 MonoBehaviour Base 0 PPtr<GameObject> m_GameObject 0 int m_FileID = 0 0 SInt64 m_PathID = 2 1 UInt8 m_Enabled = 1 0 PPtr<MonoScript> m_Script 0 int m_FileID = 1 0 SInt64 m_PathID = 103 1 string m_Name = "" 1 UInt8 myBool = 1 1 string myStr = "字符串" 0 int myInt = 12345
可以看到和AssetStudio中的基本一樣,如果你的中文字符串是亂碼,說明你沒有采用UTF-8編碼
【問題2】編碼問題
Unity序列化文件中的字符串用的是UTF-8編碼
改一下參數,再回到UABE,點擊Import Dump,選擇修改后的文本,可以看到Modified列中出現了星號。接下來就是保存了,點擊OK,這里有個小問題,UABE只能另存為,而不能直接寫回源文件,所以要保存到別的地方,再手動復制替換。
替換后,如果沒有其他安全措施,那就可以直接運行,效果如下:

(3)修改之二進制形式
二進制形式其實很少用,能直接改文本根本不想寫十六進制,但是以防萬一還是測試了。
還是先用UABE打開level0並定位到要改的MonoBehavior,這次點擊右側的Export Raw,用010 Editor打開,工具其實隨意,只要能進行十六進制的編輯就可以了。關於參數區域,前面(二、腳本在序列化文件中的表現形式)已經說過了,但是那是已知類的字段的情況,再不知道的情況怎么做呢?其實也不難,直接調成UTF-8編碼,雖然都是亂碼,但是字符串部分是沒有亂的!用010Editor的話效果如下:

這里有兩種方式回到十六進制:
① 010 Editor的話,選中字符串,再切回十六進制顯示,選中是保持着的
② 在百度找個在線編碼轉換,(注意【問題2】編碼問題)得到這串的編碼“%e6%96%b0%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2”,只保留十六進制數“e696b0e79a84e5ad97e7aca6e4b8b2”,然后利用編輯器搜索功能搜索一下,一樣可以定位到

確定了字符串的位置,接下來就是修改,回顧一下前面的規則,字符串是“先長度、再Byte數組、最后補0對齊4byte”的規則,所以前面的“0F 00 00 00”是長度,還是那個在線編碼轉換,得到“更新的字符串”的編碼“%e6%9b%b4%e6%96%b0%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2”,只保留十六進制數“e69bb4e696b0e79a84e5ad97e7aca6e4b8b2”,長度為18個Byte(十六進制就是0x12),按照規則寫進去:

注意,字符串后面的參數應該向后移動,不能被覆蓋。
改完了就是寫回,回到UABE,右側Import Raw,選中已經修改完的dat文件,保存,覆蓋,再運行。效果如下:

