偶久网

 找回密码
 注册会员

QQ登录

只需一步,快速开始

首页 改图教学 查看内容

魔兽改图MPQ技术内幕

2016-6-5 01:40| 发布者: 邪恶叔| 查看: 27986



在此之前,您可以先看一个MPQ文件,您必须先打开它。 
为此每您必须使用SFileOpenArchive。它会打开一个存档,并给你一个HANDLE,您可以稍后调用SFileOpenFileEx和SFileCloseArchive 。 
第一个参数,lpFileName,只不过是mpq公开的名称,绝不能为空。 
第二个参数,dwMPQID 是将指派给mpq内部的ID。这并不改变mpq ,目前还不清楚为什么这样做。 
第三个参数,dwUnknown,这是唯一我们认为没用的,但是不容忽视。 
最后一个参数,lphMPQ,是一个HADLE的指针(您必须先声明)。 
如果SFileOpenArchive成功完成,this HANDLE will be that of the MPQ。 
如果SFileOpenArchive成功,其返回值将为零。 
但是,有几种情况可能导致SFileOpenArchive失败。 
如果它失败,将返回一个值是假的。在这种情况下,您可以调用GetLastError获得更进一步的信息,为什么失败。如果lpFileName是一个零长度字符串或phMPQ是Null ,GetLastError 将返回ERROR_INVALID_PARAMETER 。如果该文件lpFileName不存在, GetLastError 将返回 ERROR_FILE_NOT_FOUND。在一些非常罕见的情况下, GetLastError可能会返回其他一些潜错误值。 


Closing an Archive - SFileCloseArchive 
关闭存档函数 - SFileCloseArchive 

BOOL WINAPI SFileCloseArchive(HANDLE hMPQ);
Parameter What it is
hMPQ [in] The HANDLE of the MPQ to close, which was acquired earlier with SFileOpenArchive. SFileCloseArchive will fail (or worse) if this is NULL or a HANDLE not obtained with SFileOpenArchive.

一旦您开启一个mpq存档,你必须记住它关闭时,您就大功告成了!SFileCloseArchive 是SFileOpenArchive 的产物。 
作为与SFileOpenArchive ,SFileCloseArchive返回一个非零值,那就是成功的 
如果返回一个假值,那就失败了。 
然而,在这种情况下,GetLastError不会提供任何有用的信息。 
-所以你只能假设原因是hMPQ参数是无效的。 

Opening a File Inside an MPQ - SFileOpenFileEx 
打开MPQ内的文件函数 - SFileOpenFileEx 


BOOL WINAPI SFileOpenFileEx(HANDLE hMPQ, LPCSTR lpFileName, DWORD dwSearchScope, HANDLE *lphFile);
Parameter What it is
hMPQ [in] The HANDLE of the MPQ previously opened with SFileOpenArchive that contains the file you want to open. SFileOpenFileEx will fail or crash if this isNULL or a HANDLE not obtained with SFileOpenArchive.
lpFileName [in] A pointer to a NULL terminated string that holds name of the file within the MPQ to be opened. SFileOpenFileEx will crash if this is NULL.
dwSearchScope [in] Specifies where to look for a file when opening a file on the hard drive. Must be NULL when working with MPQs.
lphFile [out] A pointer to a HANDLE variable that, upon successful completion, will hold the HANDLE of the requested file. SFileOpenFileEx will fail if this is NULL.

只是因为你用SFileOpenArchive并不意味着您就可以从它立即开始读取。请记住,MPQ只不过是包含其他文件的多档案文件。在您可以阅读任何一个mpq ,您必须打开一个(或多个)的MPQ内部档案。 SFileOpenFileEx就是让您使用这一任务的函数;它将打开在一个mpq所请求的文件并对其返回一个HANDLE。 
再次, sfileopenfileex将返回一个非零值就成功,是假值的就失败,您可以调用getlasterror获得的原因。如果lpfilename是一个零长度字符串或lphfile是Null , getlasterror将返回error_invalid_parameter 。如果该文件不存在于mpq , getlasterror会报告error_file_not_found 。在一些罕见的情况下, getlasterror可能会报告error_file_invalid ,并就极为罕见的情况下,它可能会返回其他一些模糊的错误值。 
重要注意事项:当您调用sfileclosearchive关闭mpq ,同样会关闭所有MPQ内部打开的文件,您获取到来自sfileopenfileex的HANDLEs成为无效。如果您调用sfilereadfile , sfilegetfilesize , sfilesetfilepointer ,或sfileclosefile,这些其中一个无效的HANDL,调用将失败,并且STROM甚至可能崩溃。 

Closing a File Inside an MPQ - SFileCloseFile 
关闭MPQ内的文件函数 - SFileCloseFile 

BOOL WINAPI SFileCloseFile(HANDLE hFile);
Parameter What it is
hFile [in] The HANDLE of the file to close, which was acquired earlier with SFileOpenFileEx. SFileCloseFile will fail (or worse) if this is NULL or a HANDLE not obtained withSFileOpenFileEx.
就像SFileCloseArchive is to SFileOpenArchive, SFileCloseFile is the natural compliment of SFileOpenFileEx, 用来关闭一个已经打开的文件. 
也想sfileclosearchive , sfileclosefile将返回一个非零值说明成功的,假值的就失败, getlasterror将不提供帮助。所幸的是,多于sfileclosearchive , sfileclosefile只在hFlie是NULL或一个无效的HANDLE。 

Reading from a File in an MPQ - SFileReadFile 
读取MPQ内文件函数 - SFileReadFile 
BOOL WINAPI SFileReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
Parameter What it is
hFile [in] The HANDLE of the file to read from, which was acquired earlier with SFileOpenFileEx. SFileReadFile will crash if this is NULL or a HANDLE not obtained withSFileOpenFileEx.
lpBuffer [out] A pointer to a buffer in memory where SFileReadFile will place the data read from the file. This buffer must be at least as large as nNumberOfBytesToRead.SFileReadFile will fail if this is NULL.
nNumberOfBytesToRead [in] The number of bytes for SFileReadFile to read from the file. SFileReadFile may crash if this is larger than the size of lpBuffer.
lpNumberOfBytesRead [out] A pointer to a DWORD that will hold the number of bytes actually read from the file. The number of bytes read will never be more than nNumberOfBytesToRead, but may be less if the number of unread bytes in the file is less than nNumberOfBytesToRead. It is not recommended to let this be NULL.
lpOverlapped [in] A pointer to an OVERLAPPED structure. This is used for asynchronous reading of files on a disk, and must be NULL when reading files in MPQs.

当然,您要先打开再读取文件。 
一旦你获得一个有效的文件HANDLE从sfileopenfileex ,这就是这个函数的功能。 sfilereadfile会读取指定的字节数,然后推进文件指针。这意味着,如果您有一个文件,您调用sfilereadfile会读取的一半的档案,当您再次调用sfilereadfile,你会得到另一半的档案。如果您需要再次读取上半部分的文件,你会需要调用sfilesetfilepointer 。 
sfilereadfile将提供一个非零的返回值就成功,或虚假的就失败。不过,你必须记住,只是因为它返回一个非零值,并不等于它实际上读取了什么,这只是说明没有错误发生。 
如果在该文件hfile有不足未读字节(字节从文件指针到文件结束)比要求数量少, sfilereadfile会读不到的数目要求字节; 
如果档案hfile的文件指针是在该文件的末尾, sfilereadfile会读什么,设置lpnumberofbytesread为0 ,返回真。因此,检查lpnumberofbytesread 是非常重要的。 

Getting a File's Size - SFileGetFileSize 
获得文件大小函数- SFileGetFileSize 
DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh);
Parameter What it is
hFile [in] The HANDLE of the file whose size is to be determined. SFileGetFileSize will crash if this is NULL or a HANDLE not obtained with SFileOpenFileEx.
lpdwFileSizeHigh [out] A pointer to a DWORD that, upon successful completion, will hold the high 32-bits (a DWORD) of the file's size. However, it is not possible for a file in an MPQ to be this large (over 4 gigabytes), so this is virtually unused, and may safely be NULL.

这是普遍认为是最坏的编程实践,因为完全可以进行修改,在开始读取时让不明长度的文件不会造成崩溃。sfilegetfilesize在这里只是把戏,因为它在用sfileopenfileex打开文件后才可以撷取文件的大小 。 
也有一些重大的故障点在sfilegetfilesize 。首先是含糊不清错误。当sfilegetfilesize成功,它将返回文件的大小(可0 ! ) 。但是,当发生错误时,它将返回0xFFFFFFFF的,而且同一件事,它会返回为4294967295字节( 4 GB的) 。所幸的是,这不是一个很大的问题,正如您可能从未真正看到一个文件这么大。第二个问题,更危险的是sfilegetfilesize缺乏错误检查。 sfilegetfilesize不检查是否hfile是有效或是否为零。这意味着,如果你给它一个hfile的无效的HANDLE ,程序崩溃,电脑也会崩溃。因此,底线是这样的:无比谨慎使用此功能。 


Moving the File Pointer - SFileSetFilePointer 
移动文件指针函数- SFileSetFilePointer 
DWORD WINAPI SFileSetFilePointer(HANDLE hFile, long nDistanceToMove, long *lpDistanceToMoveHigh, DWORD dwMoveMethod);
Parameter What it is
hFile [in] The HANDLE of the file whose file pointer is to be moved. SFileSetFilePointer will crash if this is NULL or a HANDLE not obtained with SFileOpenFileEx.
nDistanceToMove [in] The low-order 32-bits of the number of bytes for SFileSetFilePointer to move the file pointer, with positive numbers moving the pointer forward and negative numbers moving the pointer backward. This value can also be 0.
lpDistanceToMoveHigh [in] A pointer to the high-order 32-bits of the distance for SFileSetFilePointer to move the file pointer. But, because MPQs do not support files this large, this is unused andmust be NULL or SFileSetFilePointer will fail.
dwMoveMethod [in] Specifies the relative location the file pointer will be moved to. Must be one of these following values in Windows.h:

FILE_BEGIN The file pointer will be set to nDistanceToMove bytes from the beginning of the file. nDistanceToMove must be positive.
FILE_CURRENT The file pointer will be set to nDistanceToMove bytes from the current location of the file pointer.
FILE_END The file pointer will be set to nDistanceToMove bytes from the end of the file. nDistanceToMove must be negative.

要想从文件任意位置读取,就要先在该文件提出移动指针,这个功能函数就是sfilesetfilepointer。 
文件指针指向了下次读取时的读取位置。每次读取和编写将会移动文件指针到读写区域的最底部。 

sfilesetfilepointer实际上并不移动文件指针,只是在文件中对应ndistancetomove。相反, sfilesetfilepointer移动文件指针到一个相对位置,无论是开头或结尾的文件。 
举例来说,假设您有一个千字节的文件。当文件第一次打开,它的文件指针设置为0 ,则指的是第一个字节。然后,您读100个字节从该文件。然后,您调用sfilesetfilepointer设置ndistancetomove 500 。如果您调用dwmovemethod 设置为file_begin,文件指针将被设定为500 。如果你曾dwmovemethod作为file_current ,文件指针将是600 ,因为文件指针被转移到了当您从档案读取后100字节。但如果设置dwmovemethod以file_end , sfilesetfilepointer会失败,因为它将尝试设置档案指针一千四百九十九(文件中的最后字节, 999 ,加500 )不存在。如果您多用几次,会发现这是个非常简单的函数。 

假如失败, sfilesetfilepointer返回0xFFFFFFFF ,作为sfilegetfilesize也有同样的陷阱 。但是,当圆满完成, sfilesetfilepointer会返回档案hfile新的绝对位置的文件指针 。这意味着您可以简单地获取当前的立场文件指针调用sfilesetfilepointer设置ndistancetomove 0 , dwmovemethod设置为file_current 。事实上,这是为什么说不存在sfilegetfileposition功能。 

就像sfilegetfilesize , sfilesetfilepointer会肆无忌惮地使用任何你给它hfile的HANDLE,不会进行任何错误检查。这意味着,确保获得一个有效的文件处理,您必须谨慎,否则,您的电脑又会崩溃。 


Choosing a Language - SFileSetLocale 
选择语言- SFileSetLocale 

LCID WINAPI SFileSetLocale(LCID lcNewLocale);
Parameter What it is
lcNewLocale [in] The language code (LCID) that SFileSetLocale will make the new default. The following codes are ones that I've found in Starcraft MPQs:
0 Language Neutral/English
0x407 German
0x409 English
0x40a Spanish
0x40c French
0x410 Italian
0x416 Portuguese
SFileSetLocale 是功能简单,背后复杂的代表.它使用了暴雪的multilinguality系统。感谢它,一个单一的函数调用,保证所有文件读出一个mpq的语言。它的唯一参数是the entire Storm MPQ subsystem。它绝不会失败,并且它传回的语言代码你给它,不过其返回值毫无价值。 

multilinguality系统原理是这样的:每个MPQ档案有一个语言代码,并只要他们有不同的语言代码就可以有多个文件具有相同名称的。当调用sfileopenfileex时, sfileopenfileex寻找一个文件具有相同的语言代码储存,然后调用sfilesetlocale (如果sfilesetlocale从来没有被调用,语言的代码为0 ) 。如果一个文件匹配的语言代码无法找到, sfileopenfileex将打开中立语言(有一个语言代码0 )版本的文件。
第五章 
THE STARCRAFT CAMPAIGN EDITOR AND THE MPQ API LIBRARY 
星际争霸编辑器和MPQ API LIBRARY 

返回顶部