SoUI 0.5版本占坑
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
9.0 KiB

3 years ago
local ffi = require 'ffi'
ffi.cdef[[
struct SFILE_CREATE_MPQ {
unsigned long cbSize; // Size of this structure, in bytes
unsigned long dwMpqVersion; // Version of the MPQ to be created
void* pvUserData; // Reserved, must be NULL
unsigned long cbUserData; // Reserved, must be 0
unsigned long dwStreamFlags; // Stream flags for creating the MPQ
unsigned long dwFileFlags1; // File flags for (listfile). 0 = default
unsigned long dwFileFlags2; // File flags for (attributes). 0 = default
unsigned long dwFileFlags3; // File flags for (signature). 0 = default
unsigned long dwAttrFlags; // Flags for the (attributes) file. If 0, no attributes will be created
unsigned long dwSectorSize; // Sector size for compressed files
unsigned long dwRawChunkSize; // Size of raw data chunk
unsigned long dwMaxFileCount; // File limit for the MPQ
};
bool SFileCreateArchive2(const wchar_t* szMpqName, struct SFILE_CREATE_MPQ* pCreateInfo, uint32_t* phMpq);
bool SFileOpenArchive(const wchar_t* szMpqName, unsigned long dwPriority, unsigned long dwFlags, uint32_t* phMpq);
bool SFileCloseArchive(uint32_t hMpq);
bool SFileAddFileEx(uint32_t hMpq, const wchar_t* szFileName, const char* szArchivedName, unsigned long dwFlags, unsigned long dwCompression, unsigned long dwCompressionNext);
bool SFileExtractFile(uint32_t hMpq, const char* szToExtract, const wchar_t* szExtracted, unsigned long dwSearchScope);
bool SFileHasFile(uint32_t hMpq, const char* szFileName);
bool SFileSetMaxFileCount(uint32_t hMpq, unsigned long dwMaxFileCount);
bool SFileCreateFile(uint32_t hMpq, const char* szArchivedName, unsigned long long FileTime, unsigned long dwFileSize, unsigned long lcLocale, unsigned long dwFlags, uint32_t* phFile);
bool SFileWriteFile(uint32_t hFile, const void* pvData, unsigned long dwSize, unsigned long dwCompression);
bool SFileFinishFile(uint32_t hFile);
bool SFileOpenFileEx(uint32_t hMpq, const char* szFileName, unsigned long dwSearchScope, uint32_t* phFile);
bool SFileReadFile(uint32_t hFile, void* lpBuffer, unsigned long dwToRead, unsigned long* pdwRead, void* lpOverlapped);
unsigned long SFileGetFileSize(uint32_t hFile, unsigned long* pdwFileSizeHigh);
bool SFileCloseFile(uint32_t hFile);
bool SFileRemoveFile(uint32_t hMpq, const char* szFileName, unsigned long dwSearchScope);
bool SFileGetFileInfo(uint32_t hMpqOrFile, int InfoClass, void * pvFileInfo, unsigned long cbFileInfo, unsigned long* pcbLengthNeeded);
unsigned long SFileGetLocale();
unsigned long GetLastError();
]]
ffi.cdef[[
struct SYSTEMTIME {
unsigned short wYear;
unsigned short wMonth;
unsigned short wDayOfWeek;
unsigned short wDay;
unsigned short wHour;
unsigned short wMinute;
unsigned short wSecond;
unsigned short wMilliseconds;
};
struct FILETIME {
unsigned long dwLowDateTime;
unsigned long dwHighDateTime;
};
void GetSystemTime(struct SYSTEMTIME* lpSystemTime);
int SystemTimeToFileTime(const struct SYSTEMTIME* lpSystemTime, struct FILETIME*lpFileTime);
]]
local SFileMpqNumberOfFiles = 36
require 'filesystem'
local uni = require 'ffi.unicode'
local stormlib = ffi.load('stormlib')
local function current_filetime()
local systemtime = ffi.new('struct SYSTEMTIME')
local filetime = ffi.new('struct FILETIME')
ffi.C.GetSystemTime(systemtime)
if not ffi.C.SystemTimeToFileTime(systemtime, filetime) then
return 0
end
return filetime.dwLowDateTime | (filetime.dwHighDateTime << 32)
end
local wfile = {}
wfile.__index = wfile
function wfile:close()
if self.handle == 0 then
return
end
stormlib.SFileFinishFile(self.handle)
self.handle = 0
end
function wfile:write(buf)
if self.handle == 0 then
return false
end
return stormlib.SFileWriteFile(self.handle, buf, #buf, 0x02)
end
local rfile = {}
rfile.__index = rfile
function rfile:close()
if self.handle == 0 then
return
end
stormlib.SFileCloseFile(self.handle)
self.handle = 0
end
function rfile:size()
if self.handle == 0 then
return 0
end
local size_hi = ffi.new('unsigned long[1]', 0)
local size_lo = stormlib.SFileGetFileSize(self.handle, size_hi)
return size_lo | (size_hi[0] << 32)
end
function rfile:read(n)
if self.handle == 0 then
return nil
end
if not n then
n = self:size()
end
local buf = ffi.new('char[?]', n)
local pread = ffi.new('unsigned long[1]', 0)
if not stormlib.SFileReadFile(self.handle, buf, n, pread, nil) then
return nil
end
return ffi.string(buf, pread[0])
end
local archive = {}
archive.__index = archive
function archive:close()
if self.handle == 0 then
return
end
stormlib.SFileCloseArchive(self.handle)
self.handle = 0
end
function archive:add_file(name, path)
if self.handle == 0 then
return false
end
local wpath = uni.u2w(path:string())
return stormlib.SFileAddFileEx(self.handle, wpath, name,
0x00000200 | 0x80000000, -- MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING,
0x02, -- MPQ_COMPRESSION_ZLIB,
0x02 --MPQ_COMPRESSION_ZLIB
)
end
function archive:extract(name, path)
if self.handle == 0 then
return false
end
local dir = path:parent_path()
if not fs.exists(dir) then
fs.create_directories(dir)
end
local wpath = uni.u2w(path:string())
return stormlib.SFileExtractFile(self.handle, name, wpath,
0 --SFILE_OPEN_FROM_MPQ
)
end
function archive:has_file(name)
if self.handle == 0 then
return false
end
return stormlib.SFileHasFile(self.handle, name)
end
function archive:remove_file(name)
if self.handle == 0 then
return false
end
return stormlib.SFileRemoveFile(self.handle, name, 0)
end
function archive:open_file(name)
if self.handle == 0 then
return nil
end
local phandle = ffi.new('uint32_t[1]', 0)
if not stormlib.SFileOpenFileEx(self.handle, name, 0, phandle) then
return nil
end
return setmetatable({ handle = phandle[0] }, rfile)
end
function archive:create_file(name, size, filetime)
if self.handle == 0 then
return nil
end
if not filetime then
filetime = current_filetime()
end
local phandle = ffi.new('uint32_t[1]', 0)
if not stormlib.SFileCreateFile(self.handle, name, filetime, size, stormlib.SFileGetLocale(), 0x00000200 | 0x80000000, phandle) then
return nil
end
return setmetatable({ handle = phandle[0] }, wfile)
end
function archive:load_file(name)
if self.handle == 0 then
return nil
end
local file = self:open_file(name)
if not file then
return nil
end
local content = file:read()
file:close()
return content
end
function archive:save_file(name, buf, filetime)
if self.handle == 0 then
return false
end
local file = self:create_file(name, #buf, filetime)
if not file then
return false
end
file:write(buf)
file:close()
return true
end
function archive:number_of_files()
if self.handle == 0 then
return 0
end
local pinfo = ffi.new('uint32_t[1]', 0)
if not stormlib.SFileGetFileInfo(self.handle, SFileMpqNumberOfFiles, pinfo, 4, nil) then
return 0
end
return pinfo[0]
end
local m = {}
function m.open(path, readonly, filecount)
local wpath = uni.u2w(path:string())
local phandle = ffi.new('uint32_t[1]', 0)
local flag = 0
if readonly then
flag = 0x100
end
if not stormlib.SFileOpenArchive(wpath, 0, flag, phandle) then
return nil
end
if filecount then
stormlib.SFileSetMaxFileCount(phandle[0], filecount)
end
return setmetatable({ handle = phandle[0] }, archive)
end
function m.create(path, filecount, encrypt)
local wpath = uni.u2w(path:string())
local phandle = ffi.new('uint32_t[1]', 0)
local info = ffi.new('struct SFILE_CREATE_MPQ')
info.cbSize = ffi.sizeof('struct SFILE_CREATE_MPQ')
info.dwMpqVersion = 0 --MPQ_FORMAT_VERSION_1
info.pvUserData = nil
info.cbUserData = 0
info.dwStreamFlags = 0 --STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE
if encrypt then
info.dwFileFlags1 = 0
info.dwFileFlags2 = 0
info.dwFileFlags3 = 0
else
info.dwFileFlags1 = 0x80000000 --MPQ_FILE_EXISTS
info.dwFileFlags2 = 0x80000000 --MPQ_FILE_EXISTS
info.dwFileFlags3 = 0x80000000 --MPQ_FILE_EXISTS
end
info.dwAttrFlags = 7 --MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5
info.dwSectorSize = 0x10000
info.dwRawChunkSize = 0
info.dwMaxFileCount = filecount
if not stormlib.SFileCreateArchive2(wpath, info, phandle) then
return nil
end
return setmetatable({ handle = phandle[0] }, archive)
end
function m.attach(handle)
return setmetatable({ handle = handle }, archive)
end
return m