initial commit
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
#include "libNPK_StdAfx.h"
|
||||
#include "libNPK_NPKFile.h"
|
||||
#include "libNPK_Errors.h"
|
||||
#include "libEncrypt_SHA256.h"
|
||||
#include "libNPK_FileStream.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace NPK
|
||||
{
|
||||
|
||||
const char* CNPKFile::kNPKFileHeaderMagic = "NeoplePack_Bill\0";
|
||||
const char* CNPKFile::kNPKFileNameXorCode = "puchikon@neople dungeon and fighter DNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNF";
|
||||
|
||||
CNPKFile::CNPKFile()
|
||||
: m_hPakFile(NULL)
|
||||
, m_nFileCounts(0)
|
||||
{
|
||||
}
|
||||
|
||||
CNPKFile::~CNPKFile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CNPKFile::openPakFile(const char* szPackFileName)
|
||||
{
|
||||
assert(szPackFileName);
|
||||
if (!szPackFileName || szPackFileName[0] == 0)
|
||||
{
|
||||
setLastError(NPK_ERR_PARAM);
|
||||
return false;
|
||||
}
|
||||
|
||||
//close current pak file if it is open
|
||||
closePakFile();
|
||||
|
||||
m_strPackFileName = szPackFileName;
|
||||
|
||||
//open the pak file
|
||||
m_hPakFile = ::CreateFile(szPackFileName,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
0);
|
||||
|
||||
if (m_hPakFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_ACCESS, "File=%s, WinErr=%d", szPackFileName, ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
NPKFileHeader fileHeader;
|
||||
SecureZeroMemory(&fileHeader, sizeof(fileHeader));
|
||||
|
||||
//Read NPK Header
|
||||
DWORD dwReadBytes;
|
||||
if (!ReadFile(m_hPakFile, &fileHeader, sizeof(fileHeader), &dwReadBytes, 0) ||
|
||||
dwReadBytes != sizeof(fileHeader))
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check NPK Magic Header
|
||||
if(memcmp(fileHeader.szFileHeadMagic, kNPKFileHeaderMagic, kNPKFileHeaderMagicSize) != 0)
|
||||
{
|
||||
setLastError(NPK_ERR_INVALID_FILE_FORMAT, "File=%s, Magic=%s", szPackFileName, fileHeader.szFileHeadMagic);
|
||||
return false;
|
||||
}
|
||||
|
||||
//get file counts
|
||||
m_nFileCounts = fileHeader.nFileCounts;
|
||||
m_fileList.resize(m_nFileCounts);
|
||||
|
||||
// read file info
|
||||
FileInfo fileInfo;
|
||||
for (int32_t i = 0; i < m_nFileCounts; i++)
|
||||
{
|
||||
SecureZeroMemory(&fileInfo, sizeof(fileInfo));
|
||||
|
||||
if (!ReadFile(m_hPakFile, &fileInfo, sizeof(fileInfo), &dwReadBytes, 0) ||
|
||||
dwReadBytes != sizeof(fileInfo))
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
//decode file name
|
||||
for(int32_t j = 0; j < kNPKFileNameXorCodeSize; j++)
|
||||
{
|
||||
fileInfo.szFileName[j] ^= kNPKFileNameXorCode[j];
|
||||
}
|
||||
|
||||
m_fileList[i].fileName = fileInfo.szFileName;
|
||||
m_fileList[i].fileOffset = fileInfo.fileOffset;
|
||||
m_fileList[i].fileSize = fileInfo.fileSize;
|
||||
}
|
||||
|
||||
if(!checkPakFileHash())
|
||||
{
|
||||
// if hash check failed, close the pak file
|
||||
closePakFile();
|
||||
return false;
|
||||
}
|
||||
|
||||
//build hash file list
|
||||
buildHashFileList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNPKFile::checkPakFileHash()
|
||||
{
|
||||
if(m_hPakFile == NULL)
|
||||
{
|
||||
setLastError(NPK_ERR_INVALID_STATE, "NPK file is not opened");
|
||||
return false;
|
||||
}
|
||||
//calculate hash
|
||||
int32_t headSize = sizeof(NPKFileHeader) + sizeof(FileInfo) * m_nFileCounts;
|
||||
int32_t hashSize = (int32_t)(headSize / 17) * 17; // align to 17 bytes
|
||||
|
||||
// allocate hash buffer
|
||||
uint8_t* hashBuffer = new uint8_t[hashSize];
|
||||
SecureZeroMemory(hashBuffer, hashSize);
|
||||
|
||||
// copy header and file info to hash buffer
|
||||
DWORD dwReadBytes = 0;
|
||||
if (0 != ::SetFilePointer(m_hPakFile, 0, 0, FILE_BEGIN) ||
|
||||
!ReadFile(m_hPakFile, hashBuffer, hashSize, &dwReadBytes, 0) ||
|
||||
dwReadBytes != hashSize)
|
||||
{
|
||||
delete[] hashBuffer; hashBuffer = nullptr;
|
||||
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate hash
|
||||
Sha256Digest calculatedHash;
|
||||
sha256Calculate(hashBuffer, hashSize, calculatedHash);
|
||||
delete[] hashBuffer; hashBuffer = nullptr;
|
||||
|
||||
//read hash data
|
||||
if (headSize != SetFilePointer(m_hPakFile, headSize, 0, FILE_BEGIN))
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
Sha256Digest readedHashData;
|
||||
if (!ReadFile(m_hPakFile, readedHashData.data(), readedHashData.max_size(), &dwReadBytes, 0) ||
|
||||
dwReadBytes != readedHashData.max_size())
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_READ, "WinErr=%d", ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (calculatedHash != readedHashData)
|
||||
{
|
||||
setLastError(NPK_ERR_INVALID_FILE_HASH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNPKFile::buildHashFileList()
|
||||
{
|
||||
if (m_hPakFile == NULL)
|
||||
{
|
||||
setLastError(NPK_ERR_INVALID_STATE, "NPK file is not opened");
|
||||
return;
|
||||
}
|
||||
m_hashFileList.clear();
|
||||
for (int32_t i = 0; i < m_nFileCounts; i++)
|
||||
{
|
||||
const FileNode& fileNode = m_fileList[i];
|
||||
m_hashFileList[fileNode.fileName] = i;
|
||||
}
|
||||
}
|
||||
|
||||
bool CNPKFile::closePakFile(void)
|
||||
{
|
||||
//Close handle
|
||||
if (m_hPakFile)
|
||||
{
|
||||
CloseHandle(m_hPakFile); m_hPakFile = 0;
|
||||
}
|
||||
m_fileList.clear();
|
||||
m_nFileCounts = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNPKFile::getFileNode(int32_t index, INPKFile::FileNode& fileNode) const
|
||||
{
|
||||
if (index < 0 || index >= getFileCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
fileNode = m_fileList[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
IFileStream* CNPKFile::openFileStream(const char* szFileName)
|
||||
{
|
||||
assert(szFileName && m_hPakFile);
|
||||
if (!szFileName || szFileName[0] == 0)
|
||||
{
|
||||
setLastError(NPK_ERR_PARAM);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(m_hPakFile == NULL)
|
||||
{
|
||||
setLastError(NPK_ERR_INVALID_STATE, "NPK file is not opened");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t fileIndex = -1;
|
||||
auto it = m_hashFileList.find(szFileName);
|
||||
if(it==m_hashFileList.end())
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_NOT_FOUND, "File=%s not found in NPK file", szFileName);
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileIndex = it->second;
|
||||
}
|
||||
|
||||
if (fileIndex < 0 || fileIndex >= getFileCount())
|
||||
{
|
||||
setLastError(NPK_ERR_FILE_NOT_FOUND, "File=%s, FileIndex=%d, not found in NPK file", szFileName, fileIndex);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileNode& fileNode = m_fileList[fileIndex];
|
||||
CFileStream* pFileStream = new CFileStream(this, m_hPakFile, fileNode);
|
||||
|
||||
return pFileStream;
|
||||
}
|
||||
|
||||
void CNPKFile::closeFileStream(IFileStream* fileStream)
|
||||
{
|
||||
if (fileStream)
|
||||
{
|
||||
delete ((CFileStream*)fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
INPKFile* createNPKFile()
|
||||
{
|
||||
return new CNPKFile();
|
||||
}
|
||||
|
||||
void closeNPKFile(INPKFile* npkFile)
|
||||
{
|
||||
if (npkFile)
|
||||
{
|
||||
delete ((CNPKFile*)npkFile);
|
||||
npkFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user