最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網 會員登陸 & 注冊

口袋妖怪綠寶石——數據提取與代碼分析(1-字符集與文本信息的提取)

2022-08-17 10:09 作者:圍巾胖頭魚  | 我要投稿

說在前面:

????口袋妖怪綠寶石的ROM中包含了游戲的所有信息,許多信息是以列表的形式呈現的,例如精靈列表、道具列表、招式列表等。作為二進制文件,直接從ROM中提取出來的0/1序列本身沒有任何可讀性,只有在了解這些數據的結構和含義之后,才能提取出有用的信息。

????ROM中的0/1序列有的代表數據,有的代表代碼。數據又分為幾個類型,包括文本、數字、位圖、音樂音效等。本期專欄作者打算介紹ROM中文本信息(呈現為列表的文本信息)的提取。有了文本信息的列表,許多后續(xù)的數據處理都會變得容易理解。

字符集

????字符集是一個用數字(0/1序列)來編碼字符的映射表。絕大多數編程語言采用ASCII碼來對字符編碼,對C、Java、Python等編程語言熟悉的讀者們可能會記得數字48對應到字符'0',65對應到字符'A'等等。ASCII碼就是一種字符集。

????綠寶石ROM有自己的字符集,和ASCII碼并不相同。在原版綠寶石的源代碼項目文件夾內,字符集存儲在charmap.txt這個文件內,可以用VS Code打開看一下:

原版綠寶石的字符集

????使用VS Code的“文件——打開文件夾”,可以將整個綠寶石源代碼項目打開。charmap.txt位于項目的根目錄中。在這個文件中可以看到,第95行表示字符'A'被編碼到了數字BB,這個BB是十六進制數,對十六進制數還不太了解的讀者可以參考之前的專欄究極綠寶石5.3——科普向,什么是金手指(二)

????charmap.txt本身有上千行,本期專欄只需要用到它的前156行,第一行是

????含義是“空格”這個字符編碼到00,最后一行是

????這里的'$'并不是美元符號的意思,而是指一個字符串結尾的標記。熟悉C語言的讀者可以將它看做是C語言字符串結尾的那個'\0'字符。在綠寶石ROM中,每個字符串都會以FF這個字節(jié)結尾。

????舉個例子,“Hello world!”這個字符串(不包含引號)會被編碼到如下數據:

????每個字符恰好占用一個字節(jié)的空間。

????一個字節(jié)有8位二進制數,也就是256種可能。對于英文字符、數字、一些特殊符號(英文標點、鍵盤上的數學運算符號等)來說,256個選擇已經夠用了,但是對于漢字來說卻遠遠不夠。在漢化的綠寶石改版游戲中,一個漢字需要用2個字節(jié)來編碼,漢化版綠寶石游戲的字符集要比原版大得多。

????我們希望通過字符集將ROM中包含的文本信息列表提取出來,比如說上面那一串數字是怎么轉換到“Hello world!”這個字符串的。有一個方法是利用Excel表格的功能,將會在本期專欄介紹,在此之前,還需要對charmap.txt里的字符集進行一些處理。

字符集的處理

????為了使用Excel表格的功能,字符集需要處理成一個n行2列的格式,每行表示一個映射關系(編碼規(guī)則),第一列是字符,第二列是編碼。charmap.txt里面每個字符都被單引號括起來,同時字符和編碼之間還有個等號,這都是需要去掉的冗余信息。

????在將文本文件的內容復制到Excel表格時,制表符(按鍵盤上的tab鍵打印出來的字符)可以將同一行的文本分隔成不同的列,所以在處理后的字符集文件的每一行應該是“字符——制表符——編碼”這種格式。

????VS code中默認tab鍵輸入4個空格,而不是制表符,為此需要在“文件——首選項——設置”中進行修改:

????

把該選項的對勾去掉

? ? 打開設置后可以搜索“insert spaces”,在“常用設置”里面把框的對勾取消,就可以按tab鍵正常輸入制表符了。

????還可以注意到,有的符號需要不止一個字節(jié)來編碼。例如第52行:

????其實在游戲中可以看到這里的POKEBLOCK是一種特殊字體下的字符串,通常情況下它是不會出現在精靈列表、招式列表這種文本信息列表中的。這種不是單個字節(jié)的編碼不能放在一列中,直接把它刪掉就好了。還有一些特殊符號不是用單引號括起來的,為了方便處理也把它們刪掉了。

????以上就是需要對字符集進行的處理操作:刪除單引號、刪除等號并換成制表符、刪除編碼有多個字節(jié)的行。如果一行一行地處理,費時費力,我們需要利用VS code的功能來簡化這個操作過程。

????先將charmap.txt的前156行復制到一個新的文件,然后按Ctrl+F出現查找框,開啟正則表達式選項:

啟用正則表達式

????在搜索框內輸入

????這是一段正則表達式,不太了解的讀者可以參考由deerchao編寫的正則表達式教程:https://deerchao.cn/tutorials/regex/regex.htm

????它的含義是:從左到右依次匹配:單引號(')、單個字符(.)、單引號(')、若干個空格(\s+)、等號(=)、單個空格(\s)、兩個數字或字母(\w\w)。

????這樣就找到了由單引號括起來、僅編碼到一個字節(jié)的編碼規(guī)則,下面是關鍵的一步,按下Alt+Enter組合鍵,進入多行編輯模式

多行編輯模式

????這種模式下,每個被選中行的末尾都會有一個閃爍的光標。把選中的內容剪切下來,然后覆蓋到這個文件內(最方便的做法是3個快捷鍵Ctrl+X, Ctrl+A, Ctrl+V),這樣每一行就都是合適的編碼規(guī)則了。

????此時查找對話欄還沒有關閉,再按一次Alt+Enter進入多行編輯模式,此時按下鍵盤上的左右鍵,會發(fā)現光標會在所有行同時左移或者右移,此時編輯一行就等價于對所有行進行編輯。我們可以把單引號刪掉、把等號和多余的空格換成制表符:

多行編輯

????多行編輯是提高文件編輯效率的重要工具,尤其是當文件是由格式完全相同的行組成的時候,編輯一行就相當于編輯整個文件。

????現在可以把這些內容復制到Excel表格中了,最好復制到表格的第二行,第一行往往需要填寫列的說明(也被稱作表格的列頭):

復制到Excel表格中

????復制內容到Excel表格中一般都會出問題,上圖中本來空格應該對應到00,但復制過來后就變成了0,原因是Excel將它識別為數字,但我們需要把它當成純文本來對待:

將內容轉換為純文本

????即使轉換之后,B2單元格的0也沒有變成00,這時只需要再將字符集復制過來就可以正常顯示了,并且可以加上每一列的說明:

????

Excel表格中的字符集

????當前工作表可以重命名為“字符集”,以備后用。

????

將當前工作表重命名

????注:如果讀者分析的是漢化版的綠寶石游戲,這里的字符集可能會有上千行,并且每個漢字對應到的是兩個字節(jié)。

文本和編碼的相互轉換:文本到編碼

????接下來我們利用Excel表格的公式功能來實現文本和編碼的相互轉換。

????新建一個工作表作為實驗區(qū)域。我們先來看文本怎么轉換到編碼。

????口袋妖怪系列,全國圖鑒的第一只精靈是妙蛙種子,英文名叫Bulbasaur,這個信息可以直接在神奇寶貝百科的網站(https://wiki.52poke.com/wiki/%E4%B8%BB%E9%A1%B5)上復制到。在原版綠寶石游戲中,所有精靈名稱的字母都是大寫的。如果我們想在游戲的ROM中找到“妙蛙種子”在哪兒,就需要搜索它的大寫英文名。

????在Excel表格中,轉換英文字符串到大寫是很方便的,就是使用UPPER函數:

使用upper函數


????把Bulbasaur輸入到A1單元格,在A2單元格輸入

????這里的A1可以不用鍵盤輸入,而是在輸入公式的時候用左鍵點擊A1單元格,單元格的名稱會自動填充到這里。輸入公式完成后,再按回車,B1單元格就可以顯示大寫后的字符串了。

????下一步是將每個大寫字母對應到一個字節(jié)的編碼,這里要提到一個非常強大的Excel公式組合:INDEX+MATCH。

????INDEX函數的輸入是一列數據和一個下標值,輸出是該列數據在該下標處的取值,類比到編程語言中,可以看做是數組取下標的操作。

????MATCH函數的輸入是一個待查找數值和一列數據(還有第三個參數是匹配類型,通常選擇0表示精確匹配),輸出是該待查找數值在該列數據中的下標值。看上去,它的功能似乎和INDEX正好相反,一個是給下標找數值,一個是給數值找下標。

????將字符串轉換到編碼的過程,其實可以看做是這樣一系列步驟:

  • 取出字符串中的一個字符

  • 找到該字符在字符集中的位置

  • 將對應位置處的編碼拼接到結果上

????而這一過程可以用下面這個Excel公式來表達:

????仔細看一下這個公式,從大的結構上來說,是INDEX函數內部嵌套一個MATCH函數,從內到外,依次實現上述的三個步驟:

  • 取出字符串中的一個字符:使用MID函數來截取字符串,它的第一個參數是字符串所在的單元格,$B$1的寫法是為了在后面使用“自動填充”功能時,這個單元格保持不動(在行號B前面加上一個美元符號$,表示控制行不變;在列號1前面加上的美元符號表示控制列不變,行號和列號前面都加上美元符號說明控制這個單元格不變);第二個參數是字符串從什么位置開始截取,ROW函數返回一個單元格所在的行,ROW(A1)就是1,之所以寫成這樣是為了自動填充時A1可以自動變成A2,A3……。MID函數的第三個參數是截取的長度,單個字符就是1。

  • 找到該字符在字符集中的位置:這就是MATCH函數的功能,在輸入第二個參數時,可以直接用鼠標點擊“字符集”這個工作表的A列。

  • 將對應位置處的編碼拼接到結果上:“對應位置處的編碼”就是INDEX函數的功能,現在MATCH函數給我們提供了一個下標值,根據下標值可以很容易地找到對應的編碼。

字符對應的編碼

????接下來就是利用Excel的“自動填充”功能,將鼠標選中B2單元格右下角的綠色小方塊,當指針變成黑色十字后向下拖動,字符串對應的編碼就顯示出來了:

? ??

字符串編碼

????下面出現"#N/A"的地方是因為字符串起始位置超出了字符串的長度,所以也就沒有編碼結果了。這樣就實現了從文本到編碼的轉換。

????為了使用十六進制編輯器中的“字節(jié)序列查找”功能,我們可以先用VS code簡單處理一下從B2到B10的信息。

????從Excel表格中選中從B2到B10,將它復制到VS code的一個文本文件內,將所有的換行符替換為空格。在正則表達式中,換行符是“\n”(不包括引號):

將換行符替換為空格

????選擇全部替換(或者按下Ctrl+Alt+Enter快捷鍵),這就是空格隔開的字節(jié)序列。別忘了在綠寶石ROM中,字符串以FF結尾,因此再添加一個FF字節(jié),就可以復制到HxD內進行查找了。? ??

????使用HxD打開ROM文件,按下Ctrl+F進行查找,選擇第二個選項卡“字節(jié)序列”,查找方式為“從頭”,點擊“全部列出”:

字節(jié)序列查找

????搜索結果只有一個:

唯一的搜索結果

????這就是“妙蛙種子”的名稱所在的地址了。

????了解專欄究極綠寶石5.3——科普向,什么是金手指(五)的讀者們知道,這里的地址003185D3實際上是083185D3,這是因為ROM文件加載到模擬器之后,它所在的內存區(qū)域起始地址是08000000,這是一個固定的偏移。

????此時,源代碼項目中的符號表文件就起到作用了。打開源代碼的符號表文件(pokeemerald.sym.txt),可以看到083185D3位于gSpeciesNames這個名稱所在的范圍內:

精靈名稱地址

????而變量名“gSpeciesNames”表示的含義正好是“種族名稱”,它的地址從083185c8開始,到0831977c結束,長度是0x11b4個字節(jié),包含了所有精靈種族的名稱。接下來就是在這四千多個字節(jié)中(0x11b4的十進制是4532)提取出所有的精靈名稱。

文本和編碼的相互轉換:編碼到文本

????這一小節(jié)和上一小節(jié)做的事正好反過來:給定一個編碼序列,找到它對應的文本。還是先從Excel的公式開始。

????格式是這樣的:在一行內,每個字符對應的編碼從左到右排列,對應一個字符串。

????作為示例,將剛才轉換后的“妙蛙種子”英文名對應編碼放在第一行:

“妙蛙種子”英文名對應編碼

????現在第二行生成每個編碼對應的字符,這個過程利用的公式和上一小節(jié)非常類似:

????

????這里取出“字符集”這個工作表中的A列和B列時,在列號的前面加上了$符號,這是因為在此處使用“自動填充”功能時,是沿著一行去填充的,不加$符號公式會有錯誤(具體是什么錯誤讀者可以去嘗試一下)。$符號起到的是“固定”作用。

????按行“自動填充”后,結果是每個字符都以單獨的方式出現在單元格內:

????

編碼對應的字符

????本來最后一步是將這些字符拼接到一起,但是Excel的一些較老的版本并沒有方便使用的函數(TEXTJOIN函數在2016版之后才有)。因此,可以把生成的這一行復制到VS Code中去處理。

提取精靈名稱列表

????有了上面的功能,我們就可以開始著手提取文本信息列表了。本期專欄以精靈名稱列表為例。

????在上面的“文本和編碼的相互轉換:文本到編碼”小節(jié)的最后,我們在符號表中找到了“gSpeciesNames”這個表示精靈名稱列表的符號,它的地址從083185c8開始,到0831977c結束,長度是0x11b4個字節(jié),在HxD中,可以利用這個信息很方便地將這些字節(jié)復制出來。

????在HxD中,打開“編輯——選擇范圍”,或者按Ctrl+E,出現“選擇范圍”對話框,將“gSpeciesNames”的起始位置和終止位置(或者長度)填寫進去(注意地址00xxxxxx和08xxxxxx之間的轉換):

選擇范圍

????點擊“確定”后,就可以復制到VS Code中處理了。

????處理方式分三步:把所有的FF替換為換行符(讓每行表示一個精靈名稱);把所有的00刪除(因為它代表空格);把所有空格替換為制表符,這是為了復制到Excel中處理。這三個操作利用VS Code的查找/替換功能很方便實現。

????這里的替換也有個小技巧。注意到復制過去的數據是由空格分隔開的字節(jié)構成的,為了讓替換后的數據不包含多余的空格,可以利用正則表達式,具體來說:

????這里使用\s*表示0個或多個空格,這樣在替換的時候會將多余的空格一并替換掉。經過替換的效果如下:

替換后的結果

????這樣就可以直接復制到Excel表格中處理了。為了留出填寫額外信息的空間,我們選擇把數據復制到左上角是B2的單元格內,這些數據應該是以“文本”的格式復制到Excel表格中的。

復制到Excel表格中的數據

????從表格范圍來看,最右側的單元格不會超過K列,也就是說精靈名字的長度最多10個字符,這樣我們可以將公式放在第L列到第U列,然后利用自動填充功能獲得所有的編碼結果:

編碼結果

????再將編碼后的結果復制到VS Code中進行處理,去掉所有的#N/A和制表符,也就是

????精靈名稱列表就呈現出來了:

精靈名稱列表

精靈名稱列表和修改種族金手指的關系

????從網上很多發(fā)布金手指的網站來看,修改精靈種族的金手指都需要“精靈代碼”。比如想用金手指修改首位精靈為“超夢”,就需要知道“超夢”對應的精靈代碼是什么。現在我們有了精靈名稱列表,就可以利用Excel生成精靈代碼。

????將精靈名稱列表復制到Excel表格中,復制時選擇左上角為C2單元格,上面留出一行,左邊留出兩列,添加好對應的說明:

復制精靈名稱列表、生成序號

????其中第一列命名為“序號”,序號從0開始,對應的名字是一串問號,也就是0號精靈(游戲中并不存在)。利用自動填充很容易將第一列填寫成0,1,2……序列。

????第二列命名為“精靈代碼”,精靈代碼是長度為4的十六進制數,利用DEC2HEX函數將第一列直接轉換過來即可,該函數的第2個參數指定了生成十六進制數的長度,不足的用0補齊:

生成精靈代碼

? ? 有個小細節(jié)在這里提醒一下,如果在B列寫完公式,按下回車后發(fā)現并沒有出現公式的結果,而是公式本身出現在單元格中,說明這個單元格目前是“文本”格式,在上圖的右上角把它改為“常規(guī)”格式即可。

????這樣,用于金手指的精靈代碼就生成完畢了。

關于漢化版本的文本轉換

????上面舉的例子都是原版綠寶石中的英文文本。如果是漢化版的綠寶石,就需要處理編碼到2個字節(jié)的漢字。這里最大的問題就是中英文夾雜的文本,給定一串字節(jié),怎么判斷哪些字節(jié)是單個字節(jié)編碼到的英文字符,哪些字節(jié)是需要兩個合并在一起表示一個漢字呢?

????另外,上面的一系列操作雖然已經在很大程度上減少了重復的勞動,但看上去仍然很繁瑣,有沒有更簡單的方法來處理文本呢?

????答案是有的,就是寫一個程序去處理文本中可能出現的各種情況,然后按照指定格式進行輸出。按照專業(yè)的說法,這種程序稱做parser,中文翻譯可以是“分析程序”“解析器”等等。然而,“怎么編程”不是本系列專欄能夠涉及到的內容,感興趣的讀者可以在討論區(qū)進一步交流。

其他的文本

????有了精靈名稱列表,其他的文本也可以通過類似的方式找到:

  • 找到一段游戲中出現的文本,進行編碼后在ROM中查找,看看附近是不是也是文本格式的數據,將一大塊字節(jié)序列解碼到對應的文本。例如,道具列表、招式列表、特性列表等都可以通過這種方式來生成。

  • 利用符號表,找到文本所在的位置對應的符號名稱,這樣就可以精確地獲取文本列表所在的地址范圍。

????還有一類文本是對話類文本,也就是在游戲中和各種NPC對話時出現的文本。它們不是以列表的形式出現,往往散落在ROM中的各處,這些文本的提取需要等到后續(xù)專欄,說到“腳本”這個題目的時候再做說明。

????本期專欄涉及到的知識比較雜亂,既有文本編輯器中的多行編輯、正則表達式查找,又有Excel表格里的公式運用、自動填充,還有ROM文件的字節(jié)序列查找、范圍選擇。方法雖然雜亂,目的只有一個:高效地提取ROM信息。對于不希望使用編程(寫一個parser)來實現信息提取的讀者,作者在本專欄給出一些采用其他工具的方式作為替代,雖然從某種程度上來說,Excel的公式和查找用的正則表達式和編程也相去不遠了。

????手機上有個應用(APP)叫做“口袋改版工具”,讓它讀取ROM文件之后就可以直接獲得各種游戲信息。這是一個對用戶十分友好的應用程序,但是它屏蔽了很多細節(jié),對改版程度比較大的ROM也無能為力。本系列專欄還是希望盡可能從接近原理的方面來解釋這些信息從何而來。

????聽作者啰嗦不易,感謝眾位讀者的支持!


口袋妖怪綠寶石——數據提取與代碼分析(1-字符集與文本信息的提取)的評論 (共 條)

分享到微博請遵守國家法律
房产| 南江县| 多伦县| 开阳县| 边坝县| 玛纳斯县| 共和县| 岗巴县| 饶平县| 阳信县| 泰安市| 贵溪市| 兴义市| 肥城市| 广元市| 高邮市| 玛多县| 安宁市| 新沂市| 靖边县| 郓城县| 朝阳区| 青川县| 盈江县| 泾川县| 讷河市| 营山县| 北票市| 盐池县| 青浦区| 越西县| 萝北县| 綦江县| 保亭| 江都市| 四平市| 宜州市| 阿拉尔市| 山东省| 贵定县| 扶风县|