Quake1和2的pak资源文件格式说明
Quake1和Quake2的源碼都已經公開了,Github上可找到。源碼里邊有對pak文件解析的代碼,不過我從這里paklib找到一個別人獨立出來的一個庫,代碼不多:包括創建pak文件、向pak文件中添加資源、從pak文件中提取資源等。我看了下,然后把一些東西記錄下來,以便以后忘記后回顧用。Quake3的資源包格式是pk3的,這種格式可以直接改成zip后綴,然后解壓就可以得到里邊的資源,pak格式的文件就無法通過此方法來獲取里邊的資源,所以得通過代碼來解析。
pak文件是無壓縮的,所以將資源打包進pak中并不會減小資源的體積,但是將所有資源打包到一個pak中方便攜帶,也可以阻止一些常規玩家隨意修改資源文件。
那個paklib中的代碼就不貼出來講了,有興趣的可以從那個鏈接中下載下來看看。這里只是講下其中幾個操作的原理:
要弄懂操作的原理,首先得清楚pak包的格式,它的格式其實很簡單,給張示意圖看下就懂了
有了上面的信息,我們就可以創建一個空的pak文件,然后向文件中添加資源,再從文件中獲取資源。
創建pak文件:
向pak文件中添加資源。資源可以是任意的文件,聲音、圖片、文本等。添加資源其實也很簡單,步驟就是:1、將資源通過fopen()打開到一個文件句柄中。2、從這個句柄中將數據fread()到一個內存中。3、將該內存中的數據fwrite()到pak文件句柄中。4、更新pak文件頭和結尾數組的信息就行了。注意:資源在pak中是可以帶"路徑"的,例如將foo.png添加到pak中的images/foo.png。代碼和paklib中差不多,可能不全,僅供演示。
const char* pak = "resource.pak"; const char* file = "foo.png"; FILE* fPak = fopen(pak, "wb"); FILE* fFile = fopen(file, "rb");// 獲取資源大小 fseek(fFile, 0, SEEK_END); int iFileLength = ftell(fFile);// 將文件句柄中的數據讀到內存中 char buffer[iFileLength]; rewind(fFile); fread(buffer, 1, iFileLength, fFile);struct pakheader header; struct dirsection section; fread(&header, sizeof(header), 1, fPak); if (header.dir_length == 0) { // pak文件中還不存在一個資源// 將內存中的數據寫到pak文件句柄中fseek(fPak, sizeof(header), SEEK_SET);fwrite(buffer, 1, iFileLength, fPak);// 更新pak文件頭header.dir_offset = sizeof(header) + sizeof(char) * iFileLength;header.dir_length = sizeof(struct dirsection);fseek(fPak, 0, SEEK_SET);fwrite(&header, sizeof(header), 1, fPak);// 更新pak文件結尾數組strcpy(section.file_name, "images/foo.png");section.file_position = sizeof(header);section.file_length = iFileLength;fseek(fPak, header.dir_offset, SEEK_SET);fwrite(§ion, sizeof(section), 1, fPak); } else { // pak文件中已有資源// 讀取結尾數組的數據到內存中int count = header.dir_length / sizeof(struct dirsection) - sizeof(header);struct dirsection sections[count + 1];fseek(fPak, header.dir_offset, SEEK_SET);fread(sections, sizeof(struct dirsection), count);// 將內存中的數據寫到pak文件句柄中fseek(fPak, header.dir_offset, SEEK_SET);fwrite(buffer, 1, iFileLength, fPak);// 更新pak文件頭header.dir_offset = header.dir_offset + iFileLength;header.dir_length = header.dir_length + sizeof(struct dirsection);rewind(fPak);fwrite(&header, sizeof(header), 1, fPak);// 更新結尾數組strcpy(sections[count].file_name, "images/foo.png");sections[count].file_position = header.dir_offset - iFileLength;sections[count].file_length = iFileLength;fseek(fPak, header.dir_offset, SEEK_SET);fwrite(sections, sizeof(struct dirsection), count + 1, fPak); }fclose(fPak); fclose(fFile);從pak文件中讀取資源到內存中或解析出來。通過給定的資源名稱(和pak中的一樣,包括路徑),從pak中讀取資源到內存中的操作和添加操作差不多,先讀取結尾的數組,然后遍歷該數組,判斷數組元素中的file_name和所給的資源名稱是否相同,最后根據該元素的相關信息從pak文件句柄中讀取數據到內存中,也可以將內存中的數據fwrite()到一個文件,這樣就解析到磁盤了。因為是用字符來查找元素的,所以無法通過二分法來查找,只能遍歷,因此效率會有點低。
const char* szDstFile = "images/foo.png"; FILE* fPak = fopen("resource.pak", "r"); struct pakheader header; fread(&header, sizeof(header), 1, fPak); int count = header.dir_offset / sizeof(struct dirsection) - sizeof(header); struct dirsection sections[count]; fseek(fPak, header.dir_offset, SEEK_SET); fread(§ions, sizeof(struct dirsection), count, fPak);FILE* fFile = fopen("foo.png", "wb"); for (int i = 0; i < count; i++) {if (strcmp(sections[i].file_name, szDstFile) == 0) {char buffer[section[i].file_length];fseek(fPak, sections[i].file_position, SEEK_SET);fread(buffer, sections[i].file_length, 1, fPak);fwrite(buffer, sections[i].file_length, 1, fFile);break;} }fclose(fPak); fclose(fFile);上面的代碼很亂,也沒經過調試,不過原理就是這樣了。 : )
總結
以上是生活随笔為你收集整理的Quake1和2的pak资源文件格式说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 屏蔽硬盘坏道(HDTune+diskge
- 下一篇: ubuntu常用软件