日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Quake1和2的pak资源文件格式说明

發布時間:2023/12/14 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Quake1和2的pak资源文件格式说明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Quake1和Quake2的源碼都已經公開了,Github上可找到。源碼里邊有對pak文件解析的代碼,不過我從這里paklib找到一個別人獨立出來的一個庫,代碼不多:包括創建pak文件、向pak文件中添加資源、從pak文件中提取資源等。我看了下,然后把一些東西記錄下來,以便以后忘記后回顧用。Quake3的資源包格式是pk3的,這種格式可以直接改成zip后綴,然后解壓就可以得到里邊的資源,pak格式的文件就無法通過此方法來獲取里邊的資源,所以得通過代碼來解析。

pak文件是無壓縮的,所以將資源打包進pak中并不會減小資源的體積,但是將所有資源打包到一個pak中方便攜帶,也可以阻止一些常規玩家隨意修改資源文件。

那個paklib中的代碼就不貼出來講了,有興趣的可以從那個鏈接中下載下來看看。這里只是講下其中幾個操作的原理:
要弄懂操作的原理,首先得清楚pak包的格式,它的格式其實很簡單,給張示意圖看下就懂了

// pak文件頭,占12個字節 struct pakheader {char head[4]; // 開頭4個字節,固定存放"PACK"4個字符,用于確定是否為pak資源文件unsigned int dir_offset; // 描述除結尾那個數組外,前面的所有字節數,用于確定結尾數組的起始位置unsigned int dir_length; // 描述結尾那個數組總共多少個字節,除以數組元素的大小就等于中間存放資源的個數了 }; // 數組元素結構 struct dirsection {char file_name[56]; // 資源名稱,包括路徑和文件名,不超過56字節unsigned int file_position; // 資源在整個pak文件中的位置,也就是從第幾個字節開始unsigned int file_length; // 資源的大小,也就是該資源在pak中占幾個字節 };

有了上面的信息,我們就可以創建一個空的pak文件,然后向文件中添加資源,再從文件中獲取資源。
創建pak文件:

const char* pak = "resource.pak"; // 在磁盤中創建一個resource.pak文件(假設磁盤中還不存在該文件) FILE* fp = fopen(pak, "wb"); struct pakheader header = {.head = "PACK",.dir_offset = 0;.dir_length = 0; }; // 將文件頭信息寫到pak文件中 fwrite(&header, sizeof(header), 1, fp);

向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(&section, 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(&sections, 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资源文件格式说明的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。