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 :)
|
// first 3 bits are 010 anyhow :)
|
||||||
#define SAA_FILE_ID 0x83433
|
#define SAA_FILE_ID 0x83433
|
||||||
#define SAA_FILE_VERSION 2
|
#define SAA_FILE_VERSION 2
|
||||||
|
#define SAA_BLOCK_SIZE 2048
|
||||||
|
|
||||||
#define SAA_MAX_ENTRIES 256
|
#define SAA_MAX_ENTRIES 256
|
||||||
|
|
||||||
@ -16,7 +17,15 @@
|
|||||||
typedef struct _SAA_ENTRY
|
typedef struct _SAA_ENTRY
|
||||||
{
|
{
|
||||||
DWORD dwFileNameHash;
|
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;
|
} SAA_ENTRY;
|
||||||
|
|
||||||
typedef struct _SAA_FILE_HEADER
|
typedef struct _SAA_FILE_HEADER
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
#include "Signer.h"
|
#include "Signer.h"
|
||||||
#include "Hasher.h"
|
#include "Hasher.h"
|
||||||
#include "TinyEncrypt.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()
|
void CArchiveFS::LoadEntries()
|
||||||
{
|
{
|
||||||
// Get the file signature, verify it... use the result to decode the entries table
|
// 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"
|
#include "../filesystem.h"
|
||||||
|
|
||||||
|
#define FS_INVALID_FILE 0xFFFFFFFF
|
||||||
|
|
||||||
typedef struct _AFS_ENTRYBT_NODE
|
typedef struct _AFS_ENTRYBT_NODE
|
||||||
{
|
{
|
||||||
SAA_ENTRY* pEntry;
|
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;
|
} AFS_ENTRYBT_NODE;
|
||||||
|
|
||||||
class CArchiveFS // size: 2357
|
class CArchiveFS // size: 2357
|
||||||
@ -69,20 +100,22 @@ private:
|
|||||||
|
|
||||||
void LoadEntries();
|
void LoadEntries();
|
||||||
|
|
||||||
|
static DWORD ms_dwHashInit;
|
||||||
|
DWORD HashString(PCHAR szString);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CArchiveFS(void);
|
CArchiveFS(void);
|
||||||
CArchiveFS(DWORD dwNumEntries, DWORD dwFDSize);
|
CArchiveFS(DWORD dwNumEntries, DWORD dwFDSize);
|
||||||
|
virtual ~CArchiveFS(void);
|
||||||
|
|
||||||
virtual bool Load(char* szFileName);
|
virtual bool Load(char* szFileName);
|
||||||
|
virtual bool Load(BYTE* pbData, DWORD nLength);
|
||||||
|
virtual void Unload();
|
||||||
|
|
||||||
// TODO: CArchiveFS vftable 100E9AA8
|
virtual DWORD GetFileIndex(DWORD dwFileHash);
|
||||||
void CArchiveFS__sub_10065590() {};
|
virtual DWORD GetFileIndex(char* szFileName);
|
||||||
void CArchiveFS__sub_100654A0() {};
|
virtual DWORD GetFileSize(DWORD dwFileIndex);
|
||||||
void CArchiveFS__sub_10064E10() {};
|
virtual BYTE* GetFileData(DWORD dwFileIndex);
|
||||||
void CArchiveFS__sub_10064EC0() {};
|
|
||||||
void CArchiveFS__sub_10064F20() {};
|
virtual void UnloadData(DWORD dwFileIndex);
|
||||||
void CArchiveFS__sub_10064F60() {};
|
|
||||||
void CArchiveFS__sub_10064D30() {};
|
|
||||||
void CArchiveFS__sub_10064E40() {};
|
|
||||||
void CArchiveFS__sub_10065150() {};
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user