mirror of
https://github.com/dashr9230/SA-MP.git
synced 2024-12-22 22:47:29 +08:00
[saco] Implement CArchiveFS class member functions
This commit is contained in:
parent
d0e488b35e
commit
17aa0c8eee
@ -8,6 +8,7 @@
|
||||
// first 3 bits are 010 anyhow :)
|
||||
#define SAA_FILE_ID 0x83433
|
||||
#define SAA_FILE_VERSION 2
|
||||
#define SAA_BLOCK_SIZE 2048
|
||||
|
||||
#define SAA_MAX_ENTRIES 256
|
||||
|
||||
@ -16,7 +17,15 @@
|
||||
typedef struct _SAA_ENTRY
|
||||
{
|
||||
DWORD dwFileNameHash;
|
||||
int field_4;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD dwPrevEntry : 8; // index to previous entry (link to fake entry if none)
|
||||
DWORD dwFileSize : 24; // 24bits = max filesize of 16mb
|
||||
};
|
||||
DWORD dwDataBlock;
|
||||
};
|
||||
} SAA_ENTRY;
|
||||
|
||||
typedef struct _SAA_FILE_HEADER
|
||||
|
@ -6,6 +6,11 @@
|
||||
#include "Signer.h"
|
||||
#include "Hasher.h"
|
||||
#include "TinyEncrypt.h"
|
||||
#include "Obfuscator.h"
|
||||
|
||||
//------------------------------------
|
||||
|
||||
DWORD CArchiveFS::ms_dwHashInit = OBFUSCATE_DATA(0x9E3779B9);
|
||||
|
||||
//------------------------------------
|
||||
|
||||
@ -29,6 +34,77 @@ CArchiveFS::CArchiveFS(DWORD dwNumEntries, DWORD dwFDSize)
|
||||
|
||||
//------------------------------------
|
||||
|
||||
CArchiveFS::~CArchiveFS(void)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
DWORD CArchiveFS::HashString(PCHAR szString)
|
||||
{
|
||||
// This is an implementation of the Jenkins hash
|
||||
|
||||
# define mix(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
register BYTE* k = (BYTE*)szString;
|
||||
register DWORD initval = 0x12345678;
|
||||
register DWORD length;
|
||||
|
||||
length = (DWORD)strlen(szString);
|
||||
|
||||
register DWORD a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = UNOBFUSCATE_DATA(ms_dwHashInit); /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 12)
|
||||
{
|
||||
a += (k[0] +((DWORD)k[1]<<8) +((DWORD)k[2]<<16) +((DWORD)k[3]<<24));
|
||||
b += (k[4] +((DWORD)k[5]<<8) +((DWORD)k[6]<<16) +((DWORD)k[7]<<24));
|
||||
c += (k[8] +((DWORD)k[9]<<8) +((DWORD)k[10]<<16)+((DWORD)k[11]<<24));
|
||||
mix(a,b,c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/*------------------------------------- handle the last 11 bytes */
|
||||
c += length;
|
||||
switch(len) /* all the case statements fall through */
|
||||
{
|
||||
case 11: c+=((DWORD)k[10]<<24);
|
||||
case 10: c+=((DWORD)k[9]<<16);
|
||||
case 9 : c+=((DWORD)k[8]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 8 : b+=((DWORD)k[7]<<24);
|
||||
case 7 : b+=((DWORD)k[6]<<16);
|
||||
case 6 : b+=((DWORD)k[5]<<8);
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((DWORD)k[3]<<24);
|
||||
case 3 : a+=((DWORD)k[2]<<16);
|
||||
case 2 : a+=((DWORD)k[1]<<8);
|
||||
case 1 : a+=k[0];
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
mix(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
void CArchiveFS::LoadEntries()
|
||||
{
|
||||
// Get the file signature, verify it... use the result to decode the entries table
|
||||
@ -126,3 +202,176 @@ bool CArchiveFS::Load(char* szFileName)
|
||||
|
||||
//------------------------------------
|
||||
|
||||
bool CArchiveFS::Load(BYTE* pbData, DWORD nLength)
|
||||
{
|
||||
if (m_bLoaded)
|
||||
Unload();
|
||||
|
||||
m_pStream = new CMemoryStream(pbData, nLength);
|
||||
|
||||
m_Header.Read(m_pStream);
|
||||
|
||||
m_Header.XorV2Identifier();
|
||||
|
||||
m_bLoaded = true;
|
||||
|
||||
if (!m_Header.VerifyIdentifier()) {
|
||||
Unload();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
void CArchiveFS::Unload()
|
||||
{
|
||||
if (!m_bLoaded)
|
||||
return;
|
||||
|
||||
delete m_pStream;
|
||||
m_pStream = NULL;
|
||||
|
||||
m_bLoaded = false;
|
||||
m_bEntriesLoaded = false;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
DWORD CArchiveFS::GetFileIndex(DWORD dwFileHash)
|
||||
{
|
||||
if (!m_bEntriesLoaded)
|
||||
LoadEntries();
|
||||
|
||||
AFS_ENTRYBT_NODE* node = m_EntryBTreeRoot.FindEntry(dwFileHash);
|
||||
|
||||
if (node == NULL) {
|
||||
return FS_INVALID_FILE;
|
||||
}
|
||||
|
||||
SAA_ENTRY saaEntry = *(node->pEntry); // Always make a copy of saaEntry before decrypting it
|
||||
// Otherwise, the data decryption will get messed up
|
||||
|
||||
saaEntry.dwDataBlock = UNOBFUSCATE_DATA(saaEntry.dwDataBlock) ^ (saaEntry.dwFileNameHash & this->m_dwObfsMask);
|
||||
if (node->pEntry == &(m_pEntries[saaEntry.dwPrevEntry]))
|
||||
return FS_INVALID_FILE;
|
||||
|
||||
// Okay, we got a file.
|
||||
// TODO: It might be wise at this point to start a thread to decrypt the data
|
||||
// Chances are if the index was requested, data for it will be requested.
|
||||
|
||||
// Painfully evil conversion from SAA_ENTRY* to DWORD
|
||||
DWORD* ppEntry = reinterpret_cast<DWORD*>(&node);
|
||||
return *ppEntry;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
DWORD CArchiveFS::GetFileIndex(char* szFileName)
|
||||
{
|
||||
// PRE: szFileName must be the filename only (no paths!)
|
||||
|
||||
if (!m_bEntriesLoaded)
|
||||
LoadEntries();
|
||||
|
||||
CHAR szFileNameLC[MAX_PATH];
|
||||
strcpy(szFileNameLC, szFileName);
|
||||
_strlwr(szFileNameLC);
|
||||
|
||||
DWORD dwHash = this->HashString(szFileNameLC);
|
||||
|
||||
DWORD dwIndex = GetFileIndex(dwHash);
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (dwIndex != FS_INVALID_FILE)
|
||||
{
|
||||
CHAR szDebugMsg[1024];
|
||||
sprintf(szDebugMsg, "ArchiveFS: Requested file: %s...\n", szFileNameLC);
|
||||
OutputDebugString(szDebugMsg);
|
||||
}
|
||||
#endif
|
||||
|
||||
return dwIndex;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
DWORD CArchiveFS::GetFileSize(DWORD dwFileIndex)
|
||||
{
|
||||
AFS_ENTRYBT_NODE* node = *(reinterpret_cast<AFS_ENTRYBT_NODE**>(&dwFileIndex));
|
||||
|
||||
SAA_ENTRY saaEntry = *(node->pEntry); // Make a copy!
|
||||
saaEntry.dwDataBlock = UNOBFUSCATE_DATA(saaEntry.dwDataBlock) ^ (saaEntry.dwFileNameHash & this->m_dwObfsMask);
|
||||
return saaEntry.dwFileSize;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
|
||||
BYTE* CArchiveFS::GetFileData(DWORD dwFileIndex)
|
||||
{
|
||||
CTinyEncrypt tinyEnc;
|
||||
|
||||
AFS_ENTRYBT_NODE* node = *(reinterpret_cast<AFS_ENTRYBT_NODE**>(&dwFileIndex));
|
||||
|
||||
SAA_ENTRY saaEntry = *(node->pEntry); // Make a copy!
|
||||
saaEntry.dwDataBlock = UNOBFUSCATE_DATA(saaEntry.dwDataBlock) ^ (saaEntry.dwFileNameHash & this->m_dwObfsMask);
|
||||
|
||||
DWORD dwFileSize;
|
||||
|
||||
if (node->pbData != NULL) {
|
||||
return node->pbData;
|
||||
} else {
|
||||
// Alloc memory (in blocks!)
|
||||
dwFileSize = saaEntry.dwFileSize;
|
||||
if (dwFileSize % SAA_BLOCK_SIZE != 0)
|
||||
dwFileSize += (SAA_BLOCK_SIZE - (dwFileSize % SAA_BLOCK_SIZE));
|
||||
|
||||
node->pbData = new BYTE[dwFileSize];
|
||||
|
||||
// Determine offset to data
|
||||
SAA_ENTRY prevEntry;
|
||||
DWORD dwDataOffset = m_Header.SizeOf();
|
||||
|
||||
if (saaEntry.dwPrevEntry != m_Header.headerV2.dwInvalidIndex) {
|
||||
prevEntry = saaEntry;
|
||||
do {
|
||||
prevEntry = m_pEntries[prevEntry.dwPrevEntry];
|
||||
prevEntry.dwDataBlock = UNOBFUSCATE_DATA(prevEntry.dwDataBlock) ^ (prevEntry.dwFileNameHash & this->m_dwObfsMask);
|
||||
|
||||
dwFileSize = prevEntry.dwFileSize;
|
||||
if (dwFileSize % SAA_BLOCK_SIZE != 0)
|
||||
dwFileSize += (SAA_BLOCK_SIZE - (dwFileSize % SAA_BLOCK_SIZE));
|
||||
dwDataOffset += dwFileSize;
|
||||
|
||||
} while(prevEntry.dwPrevEntry != m_Header.headerV2.dwInvalidIndex);
|
||||
}
|
||||
|
||||
m_pStream->Seek(dwDataOffset);
|
||||
|
||||
// Load the data in blocks and decrypt it
|
||||
BYTE* pbTEAKey = reinterpret_cast<BYTE*>(this->m_pEntries) +
|
||||
(saaEntry.dwFileNameHash % (sizeof(SAA_ENTRY)*m_dwNumEntries-TEA_KEY_SIZE));
|
||||
|
||||
tinyEnc.SetKey(pbTEAKey, 0);
|
||||
|
||||
for(DWORD i=0; i<saaEntry.dwFileSize; i+=SAA_BLOCK_SIZE) {
|
||||
m_pStream->Read(node->pbData+i, SAA_BLOCK_SIZE);
|
||||
tinyEnc.DecryptData(SAA_BLOCK_SIZE, node->pbData+i);
|
||||
}
|
||||
|
||||
return node->pbData;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void CArchiveFS::UnloadData(DWORD dwFileIndex)
|
||||
{
|
||||
AFS_ENTRYBT_NODE* node = *(reinterpret_cast<AFS_ENTRYBT_NODE**>(&dwFileIndex));
|
||||
|
||||
if (node->pbData != NULL)
|
||||
{
|
||||
delete[] node->pbData;
|
||||
node->pbData = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "../filesystem.h"
|
||||
|
||||
#define FS_INVALID_FILE 0xFFFFFFFF
|
||||
|
||||
typedef struct _AFS_ENTRYBT_NODE
|
||||
{
|
||||
SAA_ENTRY* pEntry;
|
||||
@ -52,6 +54,35 @@ typedef struct _AFS_ENTRYBT_NODE
|
||||
}
|
||||
}
|
||||
|
||||
_AFS_ENTRYBT_NODE* FindEntry(DWORD dwHash)
|
||||
{
|
||||
if (this->pEntry->dwFileNameHash == dwHash) {
|
||||
return this;
|
||||
} else {
|
||||
if (dwHash < this->pEntry->dwFileNameHash) {
|
||||
if (this->pLNode == NULL)
|
||||
return NULL;
|
||||
else
|
||||
return this->pLNode->FindEntry(dwHash);
|
||||
} else {
|
||||
if (this->pRNode == NULL)
|
||||
return NULL;
|
||||
else
|
||||
return this->pRNode->FindEntry(dwHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~_AFS_ENTRYBT_NODE()
|
||||
{
|
||||
if (this->pLNode != NULL)
|
||||
delete this->pLNode;
|
||||
if (this->pRNode != NULL)
|
||||
delete this->pRNode;
|
||||
if (this->pbData != NULL)
|
||||
delete[] this->pbData;
|
||||
}
|
||||
|
||||
} AFS_ENTRYBT_NODE;
|
||||
|
||||
class CArchiveFS // size: 2357
|
||||
@ -69,20 +100,22 @@ private:
|
||||
|
||||
void LoadEntries();
|
||||
|
||||
static DWORD ms_dwHashInit;
|
||||
DWORD HashString(PCHAR szString);
|
||||
|
||||
public:
|
||||
CArchiveFS(void);
|
||||
CArchiveFS(DWORD dwNumEntries, DWORD dwFDSize);
|
||||
virtual ~CArchiveFS(void);
|
||||
|
||||
virtual bool Load(char* szFileName);
|
||||
virtual bool Load(BYTE* pbData, DWORD nLength);
|
||||
virtual void Unload();
|
||||
|
||||
// TODO: CArchiveFS vftable 100E9AA8
|
||||
void CArchiveFS__sub_10065590() {};
|
||||
void CArchiveFS__sub_100654A0() {};
|
||||
void CArchiveFS__sub_10064E10() {};
|
||||
void CArchiveFS__sub_10064EC0() {};
|
||||
void CArchiveFS__sub_10064F20() {};
|
||||
void CArchiveFS__sub_10064F60() {};
|
||||
void CArchiveFS__sub_10064D30() {};
|
||||
void CArchiveFS__sub_10064E40() {};
|
||||
void CArchiveFS__sub_10065150() {};
|
||||
virtual DWORD GetFileIndex(DWORD dwFileHash);
|
||||
virtual DWORD GetFileIndex(char* szFileName);
|
||||
virtual DWORD GetFileSize(DWORD dwFileIndex);
|
||||
virtual BYTE* GetFileData(DWORD dwFileIndex);
|
||||
|
||||
virtual void UnloadData(DWORD dwFileIndex);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user