port 2D step 5a: EterPack/EterPackManager + EterBase file units against the real 40250 packs

- Mirror copies: EterPack (EterPack, EterPackManager, EterPackCursor, CSHybridCrypt policy, Inline.h)
  and EterBase logic units tea/lzo/Timer/Stl/Random, with PORT-tagged width fixes (LONG index
  fields, static_assert(sizeof(TEterPackIndex) == 192), tea_word = Win32 unsigned long).
- TEA pack keys are extracted from the reference EterPack.cpp at configure time into
  build/.../EterPackKeys.generated.h; nothing key-bearing is tracked.
- Platform EterBase: FileBase (stdio, case-insensitive fallback), MappedFile (mmap /
  MapViewOfFile), CRC32, Debug, Utils StringPath/StringLowers.
- Win32Crt: CreateDirectory/DeleteFile/_access/MAKEFOURCC; Win32MinMax.h min/max (force-included
  on MinGW, whose windows.h omits them in C++).
- tests/port_eterpack_test: registers pack/Index like PackInitialize and checks 119
  registrations / 103 packs + root / 54891 entries / 52609 paths / 2282 overrides with
  first-registered-wins, and that exactly the 4 short-layout SECURITY files fail to load.
- 2R count corrected (136 -> 119 registrations, 103 distinct packs + root).

Not runtime-reachable yet (asset_io pack backend is step 5b), so port-map statuses stay TODO.
port_gate: macos/android/ios/windows PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-23 00:56:39 +09:00
co-authored by Claude Opus 5
parent f210f47076
commit 1f1004da79
31 changed files with 4640 additions and 142 deletions
+1
View File
@@ -488,3 +488,4 @@
{"time":"2026-09-22T13:00:00Z","event":"port_map_rebaseline","unit":"UserInterface/PythonPlayerEventHandler.cpp","reset_to_todo":["CPythonPlayerEventHandler::GetSingleton","CPythonPlayerEventHandler::~CPythonPlayerEventHandler","CPythonPlayerEventHandler::OnMoving","CPythonPlayerEventHandler::OnMove","CPythonPlayerEventHandler::OnStop","CPythonPlayerEventHandler::CPythonPlayerEventHandler"],"reason":"new mirror architecture owns the CPythonPlayerEventHandler singleton in native code; prior GDScript counterparts are migration sources, not final mirror implementations","tests":["port_map.py check"]}
{"time":"2026-09-22T15:00:00Z","event":"port_map_rebaseline","scope":"all units","totals":{"logic":3343,"python":5175,"platform":2091,"all":10609,"done":0,"divergent":1},"previous":{"logic":3285,"python":4890,"platform":2229,"all":10404},"changes":["cpp_functions: strip comments, accept indented qualified definitions inside namespace blocks, long parameter/initializer lists (40-line window), all-caps qualified ctors, skip constructor initializer entries","EterBase split by unit: tea/lzo/cipher/Random/Stl/Timer/Poly are logic, CPostIt/CRC32/Debug/FileBase/FileDir/FileLoader/MappedFile/TempFile/Utils/error stay platform; 8 obsolete platform stub files removed","audit/slices regenerated (layer fields; EterLib/Dimm.h now platform)"],"reason":"PORT-PLAN 2A step 6; inventory cross-checked against the clang-derived platform skeleton: every stub is inventoried except 22 declarations 40250 never defines and namespace/typedef spelling variants","tests":["port_map.py check","script/port_gate.sh (macos/android/ios/windows PASS, linux BLOCKED)"]}
{"time":"2026-09-22T16:00:00Z","event":"pack_inventory","scope":"Client/pack (batch 2R)","tool":"tools/epk_scan/epk_scan.py","report":"audit/packs/2R-scan.json","totals":{"registered_packs":136,"entries":54891,"unique_paths":52609,"NONE":20727,"COMPRESS":29812,"SECURITY":4352,"PANAMA":0,"HYBRIDCRYPT":0,"decode_failures":0,"overrides":2282,"overrides_different":2194,"case_collisions":0},"findings":["first-registered pack wins (CEterFileDict multimap, VS2022 STL)","short-layout packer in metin2_patch_{sd,mineral,mundi}: 4 SECURITY files rejected by 40250 CRC check","no server-delivered pack keys needed"]}
{"time":"2026-09-23T10:00:00Z","event":"port_round","unit":"EterPack/*, EterBase/{tea,lzo,Timer,Stl,Random}.cpp + platform EterBase/{CRC32,FileBase,MappedFile,Debug,Utils}","ported":["EterPack/EterPack.cpp","EterPack/EterPackManager.cpp","EterPack/EterPackCursor.cpp","EterPack/EterPackPolicy_CSHybridCrypt.cpp","EterBase/tea.cpp","EterBase/lzo.cpp","EterBase/Timer.cpp","EterBase/Stl.cpp","EterBase/Random.cpp"],"status_note":"mirror copies + platform FileBase/MappedFile/CRC32/Debug/Utils; not runtime-reachable until the asset_io pack backend (PORT-PLAN §5 step 5), so port-map statuses stay TODO","divergent":[],"needs_live":[],"corrections":["2R registered_packs 136 was wrong: 124 Index pairs -> 119 RegisterPack successes, 103 distinct packs + root (PORT-PLAN, memory)"],"findings":["libc++ unordered_multimap also keeps first-registered-wins: 0 wrong winners over 2282 overrides","exactly the 4 short-layout SECURITY files fail CEterPackManager::Get","pack/Index spells Etc for ETC.eix: CEterPack::CreateIndexFile fopens directly, so the bootstrap must resolve the on-disk dbname"],"keys":"EterPack TEA keys generated at configure time into the build tree (EterPackKeys.generated.h), never committed","tests":["extension/tests/port_eterpack_test.cpp PASS (macOS, 52605 files / 2023459178 bytes)","script/port_gate.sh macos/android/ios/windows PASS"]}
+4 -3
View File
@@ -209,7 +209,7 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
`pack/Index`)→ `CEterPackManager::RegisterPack`(每个包再注册 `<name>_texcache`,最后 `RegisterRootPack("pack/root")`
`CEterPack::__BuildIndex``.eix` 全部是 `MCOZ`TEA + LZO 包着 `EPKD` v2,每条 192 字节)→ `Get2`
1. **类型统计**Index 注册的 136 个包,54,891 条,52,609 个不同路径):
1. **类型统计**Index 注册的 103 个包`root`54,891 条,52,609 个不同路径):
| 类型 | 文件数 | 存储 | 解码后 |
| --- | --- | --- | --- |
@@ -225,7 +225,8 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
移植后的 `EterPack.cpp` 需要这两组常量;按"密钥不进仓库",由构建时从参考树生成一个 gitignore 的头文件,不直接写进源码。
3. **注册顺序与覆盖**
- `Index` 第 0 行是 `PACK`,后面是 (目录, 名字) 对,共 124 个名字。
- 实际存在的包有 136 个:Index 里的包,加上两个 `metin2_patch_dragon_rock*_texcache`,再加 `root`
- 124 对里有 119 次 `RegisterPack` 找到了包(有几个名字重复出现),去重后 103 个包(含两个
`metin2_patch_dragon_rock*_texcache`),再加 `root`。2D 用移植后的 `CEterPackManager` 复核过(原先写的 136 有误)。
- 缺失的包:106 个 `_texcache`,以及 `locale_{ae,bg,ca,mx,pa,uk,us}`。40250 遇到缺失的包只记日志、继续执行。
- 目录里有、但没注册的文件,40250 永远不会读到:`Outdoor``Psyco`0 字节)、`outdoorempirebattle1`、两个 `.rdch`
`.rdch` 在源码里没有任何引用。
@@ -333,7 +334,7 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
| `mob_proto` | `proto.cpp` | **读不了**:每条 255 字节 vs 335 字节,加密同上 | 同上 |
| `item_list.txt` | `item_list.gd` | 177 行不同(m2dev 有 dummy 条目等);40250 放在 `locale/en/`m2dev 在 `locale/common/` | 按 40250 路径读取 |
| `locale_interface.txt``itemdesc.txt``skilldesc.txt``skilltable.txt` | UI、技能表 | 20 行 / 2 行 / 2 行 / 位置不同 | 按 40250 路径读取 |
| 模型、贴图、地图(PC、Monster、Outdoor 等) | 渲染 | 40250 是经典 `.eix/.epk`136 个包、52,609 个路径,全部可以本地解码(2R) | 移植 `EterPack`/`EterPackManager` 接到 `asset_io`,请求改用 40250 路径(见 2R 第 6 条) |
| 模型、贴图、地图(PC、Monster、Outdoor 等) | 渲染 | 40250 是经典 `.eix/.epk`103 个包`root`、52,609 个路径,全部可以本地解码(2R) | 移植 `EterPack`/`EterPackManager` 接到 `asset_io`,请求改用 40250 路径(见 2R 第 6 条) |
切换步骤(批次 2D):
+128 -18
View File
@@ -1,36 +1,146 @@
// Platform skeleton for EterBase/CRC32.h (40250 EterBase/CRC32.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// Platform implementation of EterBase/CRC32.h (40250 EterBase/CRC32.cpp). GetCRC32/GetCaseCRC32 are the 40250
// loops; CRCTable is the reflected IEEE 802.3 table (polynomial 0xEDB88320), which is what 40250 spells out
// literally (checked entry for entry). The file variants read through a CFileBase-style FILE* handle.
#include "EterBase/StdAfx.h"
#include "EterBase/CRC32.h"
#include "../PlatformStub.h"
#include <cstdio>
#include <vector>
auto GetCRC32(const char *, size_t) -> DWORD
namespace
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
struct CrcTable
{
DWORD v[256];
CrcTable()
{
for (DWORD n = 0; n < 256; ++n)
{
DWORD c = n;
for (int k = 0; k < 8; ++k)
c = (c & 1) ? (0xEDB88320u ^ (c >> 1)) : (c >> 1);
v[n] = c;
}
}
};
const CrcTable s_crcTable;
}
auto GetCaseCRC32(const char *, size_t) -> DWORD
#define CRCTable (s_crcTable.v)
#define DO1(buf, i) crc = CRCTable[(crc ^ buf[i]) & 0xff] ^ (crc >> 8)
#define DO2(buf, i) DO1(buf, i); DO1(buf, i + 1);
#define DO4(buf, i) DO2(buf, i); DO2(buf, i + 2);
#define DO8(buf, i) DO4(buf, i); DO4(buf, i + 4);
#define DO16(buf, i) DO8(buf, i); DO8(buf, i + 8);
DWORD GetCRC32(const char * buf, size_t len)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
DWORD crc = 0xffffffff;
if (len >= 16)
{
do
{
DO16(buf, 0);
buf += 16;
len -= 16;
} while (len >= 16);
}
if (len != 0)
{
do
{
DO1(buf, 0);
++buf;
--len;
} while (len > 0);
}
crc ^= 0xffffffff;
return crc;
}
auto GetHFILECRC32(HANDLE) -> DWORD
#ifndef UPPER
#define UPPER(c) (((c)>='a' && (c) <= 'z') ? ((c)+('A'-'a')) : (c))
#endif
#define DO1CI(buf, i) crc = CRCTable[(crc ^ UPPER(buf[i])) & 0xff] ^ (crc >> 8)
#define DO2CI(buf, i) DO1CI(buf, i); DO1CI(buf, i + 1);
#define DO4CI(buf, i) DO2CI(buf, i); DO2CI(buf, i + 2);
#define DO8CI(buf, i) DO4CI(buf, i); DO4CI(buf, i + 4);
#define DO16CI(buf, i) DO8CI(buf, i); DO8CI(buf, i + 8);
DWORD GetCaseCRC32(const char * buf, size_t len)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
DWORD crc = 0xffffffff;
if (16 <= len)
{
do
{
DO16CI(buf, 0);
buf += 16;
len -= 16;
} while (len >= 16);
}
if (0 != len)
{
do
{
DO1CI(buf, 0);
++buf;
--len;
} while (len > 0);
}
crc ^= 0xffffffff;
return crc;
}
auto GetFileCRC32(const char *) -> DWORD
DWORD GetHFILECRC32(HANDLE hFile)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
// 40250 maps the whole file from offset 0 and CRCs it; the stream position is left where it was.
FILE* fp = static_cast<FILE*>(hFile);
const long pos = std::ftell(fp);
std::fseek(fp, 0, SEEK_END);
const long size = std::ftell(fp);
std::fseek(fp, 0, SEEK_SET);
std::vector<char> data(size > 0 ? size : 0);
const size_t got = data.empty() ? 0 : std::fread(data.data(), 1, data.size(), fp);
std::fseek(fp, pos, SEEK_SET);
if (got != data.size() || data.empty())
return 0;
return GetCRC32(data.data(), data.size());
}
auto GetFileSize(const char *) -> DWORD
DWORD GetFileCRC32(const char* c_szFileName)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
FILE* fp = std::fopen(c_szFileName, "rb");
if (!fp)
return 0;
DWORD dwRetCRC32=GetHFILECRC32(fp);
std::fclose(fp);
return dwRetCRC32;
}
DWORD GetFileSize(const char* c_szFileName)
{
FILE* fp = std::fopen(c_szFileName, "rb");
if (!fp)
return 0;
std::fseek(fp, 0, SEEK_END);
const long size = std::ftell(fp);
std::fclose(fp);
return size < 0 ? 0 : static_cast<DWORD>(size);
}
+48 -11
View File
@@ -1,9 +1,17 @@
// Platform skeleton for EterBase/Debug.h (40250 EterBase/Debug.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. Implemented
// bodies follow the 40250 non-_DEBUG build (batch 2D: Trace*, TraceError to stderr).
#include "EterBase/StdAfx.h"
#include "EterBase/Debug.h"
#include "../PlatformStub.h"
#include "EterBase/Timer.h"
#include <cstdarg>
#include <cstdio>
#include <ctime>
const DWORD DEBUG_STRING_MAX_LEN = 1024;
auto SetLogLevel(UINT) -> void
{
@@ -30,29 +38,58 @@ auto Lognf(UINT, const char *, ...) -> void
MT_PLATFORM_STUB();
}
auto Trace(const char *) -> void
void Trace(const char *)
{
MT_PLATFORM_STUB();
// Non-_DEBUG 40250: written only to the log file (OpenLogFile, not implemented yet).
}
auto Tracen(const char *) -> void
void Tracen(const char *)
{
MT_PLATFORM_STUB();
// Non-_DEBUG 40250: written only to the log file (OpenLogFile, not implemented yet).
}
auto Tracenf(const char *, ...) -> void
void Tracenf(const char *, ...)
{
MT_PLATFORM_STUB();
// Non-_DEBUG 40250: written only to the log file (OpenLogFile, not implemented yet).
}
auto Tracef(const char *, ...) -> void
void Tracef(const char *, ...)
{
MT_PLATFORM_STUB();
// Non-_DEBUG 40250: written only to the log file (OpenLogFile, not implemented yet).
}
auto TraceError(const char *, ...) -> void
void TraceError(const char* c_szFormat, ...)
{
MT_PLATFORM_STUB();
#ifndef _DISTRIBUTE
char szBuf[DEBUG_STRING_MAX_LEN+2];
strncpy(szBuf, "SYSERR: ", DEBUG_STRING_MAX_LEN);
int len = strlen(szBuf);
va_list args;
va_start(args, c_szFormat);
len = _vsnprintf(szBuf + len, sizeof(szBuf) - (len + 1), c_szFormat, args) + len;
va_end(args);
// PORT: _vsnprintf returns -1 on truncation; 40250 then writes the terminator before the buffer.
if (len < 8)
len = DEBUG_STRING_MAX_LEN;
szBuf[len] = '\n';
szBuf[len + 1] = '\0';
time_t ct = time(0);
struct tm ctm = *localtime(&ct);
fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s",
ctm.tm_mon + 1,
ctm.tm_mday,
ctm.tm_hour,
ctm.tm_min,
ELTimer_GetMSec() % 60000,
szBuf + 8);
fflush(stderr);
#endif
}
auto TraceErrorWithoutEnter(const char *, ...) -> void
+146 -35
View File
@@ -1,78 +1,189 @@
// Platform skeleton for EterBase/FileBase.h (40250 EterBase/FileBase.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// Platform implementation of EterBase/FileBase.h (40250 EterBase/FileBase.cpp). 40250 wraps a Win32 file
// HANDLE; here m_hFile holds a stdio FILE* on every target, with the same open modes (read: existing file,
// shared for reading; write: read/write, created when missing, not truncated) and the same size bookkeeping.
// Names are case-insensitive as on NTFS: 40250's pack/Index says "Etc" for ETC.eix, which a case-sensitive
// file system (Android, Linux) only finds through the fallback below.
#include "EterBase/StdAfx.h"
#include "EterBase/FileBase.h"
#include "../PlatformStub.h"
#include <cstdio>
#include <string>
CFileBase::CFileBase()
#if !defined(_WIN32)
#include <dirent.h>
#include <strings.h>
#endif
// fopen, then (POSIX) resolve each path component case-insensitively when the exact spelling is missing.
static FILE* open_nocase(const char* filename, const char* mode)
{
FILE* fp = std::fopen(filename, mode);
#if !defined(_WIN32)
if (fp || !filename[0])
return fp;
std::string path(filename);
for (char& c : path)
if (c == '\\')
c = '/';
std::string resolved = path[0] == '/' ? "/" : "";
size_t pos = path[0] == '/' ? 1 : 0;
while (pos <= path.size())
{
size_t end = path.find('/', pos);
if (end == std::string::npos)
end = path.size();
const std::string part = path.substr(pos, end - pos);
pos = end + 1;
if (part.empty() || part == "." || part == "..")
{
if (!part.empty())
resolved += part + "/";
continue;
}
DIR* dir = opendir(resolved.empty() ? "." : resolved.c_str());
if (!dir)
return NULL;
std::string match;
while (dirent* e = readdir(dir))
{
if (strcasecmp(e->d_name, part.c_str()) == 0)
{
match = e->d_name;
if (match == part)
break;
}
}
closedir(dir);
if (match.empty())
return NULL;
resolved += match;
if (end != path.size())
resolved += "/";
}
fp = std::fopen(resolved.c_str(), mode);
#endif
return fp;
}
static FILE* file_of(HANDLE h)
{
return static_cast<FILE*>(h);
}
static DWORD stream_size(FILE* fp)
{
const long pos = std::ftell(fp);
std::fseek(fp, 0, SEEK_END);
const long size = std::ftell(fp);
std::fseek(fp, pos, SEEK_SET);
return size < 0 ? 0 : static_cast<DWORD>(size);
}
CFileBase::CFileBase() : m_hFile(NULL), m_dwSize(0)
{
MT_PLATFORM_STUB();
}
CFileBase::~CFileBase()
{
MT_PLATFORM_STUB();
Destroy();
}
auto CFileBase::Destroy() -> void
char * CFileBase::GetFileName()
{
MT_PLATFORM_STUB();
return m_filename;
}
auto CFileBase::Close() -> void
void CFileBase::Destroy()
{
MT_PLATFORM_STUB();
Close();
m_dwSize = 0;
}
auto CFileBase::Create(const char *, EFileMode) -> BOOL
void CFileBase::Close()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
if (m_hFile)
{
std::fclose(file_of(m_hFile));
m_hFile = NULL;
}
}
auto CFileBase::Size() -> DWORD
BOOL CFileBase::Create(const char* filename, EFileMode mode)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
Destroy();
strncpy(m_filename, filename, MAX_PATH);
FILE* fp = NULL;
if (mode == FILEMODE_WRITE)
{
// OPEN_ALWAYS + GENERIC_READ|GENERIC_WRITE: open without truncating, create when missing.
fp = open_nocase(filename, "r+b");
if (!fp)
fp = std::fopen(filename, "w+b");
}
else
fp = open_nocase(filename, "rb");
if (fp)
{
m_hFile = fp;
m_dwSize = stream_size(fp);
m_mode = mode;
return true;
}
m_hFile = NULL;
return false;
}
auto CFileBase::SeekCur(DWORD) -> void
DWORD CFileBase::Size()
{
MT_PLATFORM_STUB();
return (m_dwSize);
}
auto CFileBase::Seek(DWORD) -> void
void CFileBase::SeekCur(DWORD size)
{
MT_PLATFORM_STUB();
// SetFilePointer takes the low DWORD as a signed distance when the high part is NULL.
std::fseek(file_of(m_hFile), static_cast<LONG>(size), SEEK_CUR);
}
auto CFileBase::GetPosition() -> DWORD
void CFileBase::Seek(DWORD offset)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
if (offset > m_dwSize)
offset = m_dwSize;
std::fseek(file_of(m_hFile), static_cast<long>(offset), SEEK_SET);
}
auto CFileBase::Write(const void *, int) -> BOOL
DWORD CFileBase::GetPosition()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
return static_cast<DWORD>(std::ftell(file_of(m_hFile)));
}
auto CFileBase::Read(void *, int) -> BOOL
BOOL CFileBase::Write(const void* src, int bytes)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
FILE* fp = file_of(m_hFile);
if (std::fwrite(src, 1, bytes, fp) != static_cast<size_t>(bytes))
return false;
std::fflush(fp);
m_dwSize = stream_size(fp);
return true;
}
auto CFileBase::GetFileName() -> char *
BOOL CFileBase::Read(void* dest, int bytes)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<char *>();
// ReadFile succeeds on a short read (end of file); only an I/O error fails.
FILE* fp = file_of(m_hFile);
std::fread(dest, 1, bytes, fp);
return !std::ferror(fp);
}
auto CFileBase::IsNull() -> BOOL
BOOL CFileBase::IsNull()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
return !m_hFile ? true : false;
}
+214 -49
View File
@@ -1,107 +1,272 @@
// Platform skeleton for EterBase/MappedFile.h (40250 EterBase/MappedFile.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// Platform implementation of EterBase/MappedFile.h (40250 EterBase/MappedFile.cpp). Everything but Map/Unmap
// is the 40250 code; the view is a POSIX mmap (CreateFileMapping/MapViewOfFile on Windows) of the CFileBase
// stream, aligned down to the allocation granularity exactly as 40250 does.
#include "EterBase/StdAfx.h"
#include "EterBase/MappedFile.h"
#include "EterBase/Debug.h"
#include "../PlatformStub.h"
#include <cstdio>
CMappedFile::CMappedFile()
#if defined(_WIN32)
#include <io.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
static DWORD allocation_granularity()
{
#if defined(_WIN32)
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
return SysInfo.dwAllocationGranularity;
#else
return static_cast<DWORD>(sysconf(_SC_PAGESIZE));
#endif
}
CMappedFile::CMappedFile() :
m_hFM(NULL),
m_lpMapData(NULL),
m_dataOffset(0),
m_mapSize(0),
m_seekPosition(0),
m_pLZObj(NULL),
m_pbBufLinkData(NULL),
m_dwBufLinkSize(0),
m_pbAppendResultDataBlock(NULL),
m_dwAppendResultDataSize(0)
{
MT_PLATFORM_STUB();
}
CMappedFile::~CMappedFile()
{
MT_PLATFORM_STUB();
Destroy();
}
auto CMappedFile::Link(DWORD, const void *) -> void
BOOL CMappedFile::Create(const char * filename)
{
MT_PLATFORM_STUB();
Destroy();
return CFileBase::Create(filename, FILEMODE_READ);
}
auto CMappedFile::Create(const char *) -> BOOL
BOOL CMappedFile::Create(const char * filename, const void** dest, int offset, int size)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
if (!CMappedFile::Create(filename))
return FALSE;
int ret = Map(dest, offset, size);
return (ret) > 0;
}
auto CMappedFile::Create(const char *, const void **, int, int) -> BOOL
LPCVOID CMappedFile::Get()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
return m_lpData;
}
auto CMappedFile::Get() -> LPCVOID
void CMappedFile::Link(DWORD dwBufSize, const void* c_pvBufData)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<LPCVOID>();
m_dwBufLinkSize=dwBufSize;
m_pbBufLinkData=(BYTE*)c_pvBufData;
}
auto CMappedFile::Destroy() -> void
void CMappedFile::BindLZObject(CLZObject * pLZObj)
{
MT_PLATFORM_STUB();
assert(m_pLZObj == NULL);
m_pLZObj = pLZObj;
Link(m_pLZObj->GetSize(), m_pLZObj->GetBuffer());
}
auto CMappedFile::Seek(DWORD, int) -> int
void CMappedFile::BindLZObjectWithBufferedSize(CLZObject * pLZObj)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<int>();
assert(m_pLZObj == NULL);
m_pLZObj = pLZObj;
Link(m_pLZObj->GetBufferSize(), m_pLZObj->GetBuffer());
}
auto CMappedFile::Map(const void **, int, int) -> int
BYTE* CMappedFile::AppendDataBlock( const void* pBlock, DWORD dwBlockSize )
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<int>();
if( m_pbAppendResultDataBlock )
{
delete []m_pbAppendResultDataBlock;
}
//realloc
m_dwAppendResultDataSize = m_dwBufLinkSize+dwBlockSize;
m_pbAppendResultDataBlock = new BYTE[m_dwAppendResultDataSize];
memcpy(m_pbAppendResultDataBlock, m_pbBufLinkData, m_dwBufLinkSize );
memcpy(m_pbAppendResultDataBlock + m_dwBufLinkSize, pBlock, dwBlockSize );
//redirect
Link(m_dwAppendResultDataSize, m_pbAppendResultDataBlock);
return m_pbAppendResultDataBlock;
}
auto CMappedFile::Size() -> DWORD
void CMappedFile::Destroy()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
if (m_pLZObj)
{
delete m_pLZObj;
m_pLZObj = NULL;
}
if (NULL != m_lpMapData)
{
Unmap(m_lpMapData);
m_lpMapData = NULL;
}
#if defined(_WIN32)
if (NULL != m_hFM)
{
CloseHandle(m_hFM);
m_hFM = NULL;
}
#endif
if( m_pbAppendResultDataBlock )
{
delete []m_pbAppendResultDataBlock;
m_pbAppendResultDataBlock = NULL;
}
m_dwAppendResultDataSize = 0;
m_pbBufLinkData = NULL;
m_dwBufLinkSize = 0;
m_seekPosition = 0;
m_dataOffset = 0;
m_mapSize = 0;
CFileBase::Destroy();
}
auto CMappedFile::GetPosition() -> DWORD
int CMappedFile::Seek(DWORD offset, int iSeekType)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
switch (iSeekType)
{
case SEEK_TYPE_BEGIN:
if (offset > m_dwSize)
offset = m_dwSize;
m_seekPosition = offset;
break;
case SEEK_TYPE_CURRENT:
m_seekPosition = min(m_seekPosition + offset, Size());
break;
case SEEK_TYPE_END:
m_seekPosition = max(0, Size() - offset);
break;
}
return m_seekPosition;
}
auto CMappedFile::Read(void *, int) -> BOOL
int CMappedFile::Map(const void **dest, int offset, int size)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
m_dataOffset = offset;
if (size == 0)
m_mapSize = m_dwSize;
else
m_mapSize = size;
if (m_dataOffset + m_mapSize > m_dwSize)
return 0;
DWORD dwSysGran = allocation_granularity();
DWORD dwFileMapStart = (m_dataOffset / dwSysGran) * dwSysGran;
DWORD dwMapViewSize = (m_dataOffset % dwSysGran) + m_mapSize;
INT iViewDelta = m_dataOffset - dwFileMapStart;
FILE* fp = static_cast<FILE*>(m_hFile);
#if defined(_WIN32)
m_hFM = CreateFileMapping((HANDLE) _get_osfhandle(_fileno(fp)), NULL, PAGE_READONLY, 0,
m_dataOffset + m_mapSize, NULL);
if (!m_hFM)
{
OutputDebugString("CMappedFile::Map !m_hFM\n");
return 0;
}
m_lpMapData = MapViewOfFile(m_hFM, FILE_MAP_READ, 0, dwFileMapStart, dwMapViewSize);
if (!m_lpMapData)
{
TraceError("CMappedFile::Map !m_lpMapData %lu", GetLastError());
return 0;
}
#else
// A zero-length view (empty file) is a valid 40250 map; mmap rejects length 0.
if (dwMapViewSize == 0)
dwMapViewSize = 1;
void* p = mmap(NULL, dwMapViewSize, PROT_READ, MAP_PRIVATE, fileno(fp), static_cast<off_t>(dwFileMapStart));
if (p == MAP_FAILED)
{
TraceError("CMappedFile::Map !m_lpMapData %s", GetFileName());
return 0;
}
m_lpMapData = p;
#endif
m_lpData = (char*) m_lpMapData + iViewDelta;
*dest = (char*) m_lpData;
m_seekPosition = 0;
Link(m_mapSize, m_lpData);
return (m_mapSize);
}
auto CMappedFile::GetSeekPosition() -> DWORD
BYTE * CMappedFile::GetCurrentSeekPoint()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
return m_pbBufLinkData+m_seekPosition;
}
auto CMappedFile::BindLZObject(CLZObject *) -> void
DWORD CMappedFile::Size()
{
MT_PLATFORM_STUB();
return m_dwBufLinkSize;
}
auto CMappedFile::BindLZObjectWithBufferedSize(CLZObject *) -> void
DWORD CMappedFile::GetPosition()
{
MT_PLATFORM_STUB();
return m_dataOffset;
}
auto CMappedFile::AppendDataBlock(const void *, DWORD) -> BYTE *
BOOL CMappedFile::Read(void * dest, int bytes)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BYTE *>();
if (m_seekPosition + bytes > Size())
return FALSE;
memcpy(dest, GetCurrentSeekPoint(), bytes);
m_seekPosition += bytes;
return TRUE;
}
auto CMappedFile::GetCurrentSeekPoint() -> BYTE *
DWORD CMappedFile::GetSeekPosition(void)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BYTE *>();
return m_seekPosition;
}
auto CMappedFile::Unmap(LPCVOID) -> void
void CMappedFile::Unmap(LPCVOID data)
{
MT_PLATFORM_STUB();
#if defined(_WIN32)
if (!UnmapViewOfFile(data))
TraceError("CMappedFile::Unmap - Error");
#else
// Same view length Map computed from the (unchanged) offset and size.
const DWORD dwSysGran = allocation_granularity();
DWORD dwMapViewSize = (m_dataOffset % dwSysGran) + m_mapSize;
if (dwMapViewSize == 0)
dwMapViewSize = 1;
if (munmap(const_cast<void*>(data), dwMapViewSize) != 0)
TraceError("CMappedFile::Unmap - Error");
#endif
m_lpData = NULL;
}
+43 -16
View File
@@ -1,7 +1,9 @@
// Platform skeleton for EterBase/Utils.h (40250 EterBase/Utils.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. Implemented
// bodies are the 40250 code (batch 2D: the path helpers EterPack uses); stl_lowers lives in EterBase/Stl.cpp.
#include "EterBase/StdAfx.h"
#include "EterBase/Utils.h"
#include "EterBase/Stl.h"
#include "../PlatformStub.h"
@@ -36,11 +38,6 @@ auto GetIndexingName(char *, DWORD) -> void
MT_PLATFORM_STUB();
}
auto stl_lowers(std::string &) -> void
{
MT_PLATFORM_STUB();
}
auto GetOnlyFileName(const char *, std::string &) -> void
{
MT_PLATFORM_STUB();
@@ -73,29 +70,59 @@ auto GetWorkingFolder(std::string &) -> void
MT_PLATFORM_STUB();
}
auto StringLowers(char *) -> void
void StringLowers(char * String)
{
MT_PLATFORM_STUB();
for (DWORD i = 0; i < strlen(String); ++i)
{
String[i] = korean_tolower(String[i]);
}
}
auto StringPath(std::string &) -> void
void StringPath(std::string & rString)
{
MT_PLATFORM_STUB();
for (DWORD i = 0; i < rString.length(); ++i)
{
if (rString[i] == '\\')
rString[i] = '/';
else
rString[i] = korean_tolower(rString[i]);
}
}
auto StringPath(char *) -> void
void StringPath(char * pString)
{
MT_PLATFORM_STUB();
for (DWORD i = 0; i < strlen(pString); ++i)
{
if (pString[i] == '\\')
pString[i] = '/';
else
pString[i] = korean_tolower(pString[i]);
}
}
auto StringPath(const char *, char *) -> void
void StringPath(const char * c_szSrc, char * szDest)
{
MT_PLATFORM_STUB();
for (DWORD i = 0; i < strlen(c_szSrc); ++i)
{
if (c_szSrc[i] == '\\')
szDest[i] = '/';
else
szDest[i] = korean_tolower(c_szSrc[i]);
}
}
auto StringPath(const char *, std::string &) -> void
void StringPath(const char * c_szSrc, std::string & rString)
{
MT_PLATFORM_STUB();
rString = "";
rString.resize(strlen(c_szSrc));
for (DWORD i = 0; i < strlen(c_szSrc); ++i)
{
if (c_szSrc[i] == '\\')
rString[i] = '/';
else
rString[i] = korean_tolower(c_szSrc[i]);
}
}
auto PrintAsciiData(const void *, int) -> void
+50
View File
@@ -30,6 +30,44 @@ if(NOT MSVC)
endif()
target_compile_options(port_logic PUBLIC ${MT_PORT_WARNING_FLAGS})
target_compile_features(port_logic PUBLIC cxx_std_20)
# Armadillo nanomite markers (StdAfx.h `__asm _emit` jumps over a signature): x86-32 MSVC inline asm that no
# target here accepts, and a no-op without the Armadillo wrapper, so they expand to nothing.
target_compile_definitions(port_logic PUBLIC "NANOBEGIN=" "NANOEND=")
if(MINGW)
target_compile_options(port_logic PUBLIC -include ${CMAKE_CURRENT_SOURCE_DIR}/common/Win32MinMax.h)
endif()
# EterPack/EterPack.cpp includes EterPackKeys.generated.h in place of the two TEA key arrays: the keys are
# read from the reference EterPack.cpp at configure time and written into the build tree, never the repository.
# Without the reference the arrays are zero and every COMPRESS/SECURITY entry fails to decode.
set(MT_40250_SOURCE "$ENV{MT_40250_SOURCE}" CACHE PATH "40250 ClientVS22/source reference root")
if(NOT MT_40250_SOURCE)
set(MT_40250_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/../../../../40250/Server Client TMP4/ClientVS22/source")
endif()
set(MT_PORT_GENERATED ${CMAKE_CURRENT_BINARY_DIR}/generated)
set(mt_eterpack_ref "${MT_40250_SOURCE}/EterPack/EterPack.cpp")
set(mt_eterpack_keys "")
if(EXISTS "${mt_eterpack_ref}")
file(READ "${mt_eterpack_ref}" mt_ref_text)
foreach(arr s_adwEterPackKey s_adwEterPackSecurityKey)
string(REGEX MATCH "static DWORD ${arr}\\[\\][ \t\r\n]*=[ \t\r\n]*{[^}]*}" block "${mt_ref_text}")
string(REGEX MATCHALL "[0-9]+" values "${block}")
list(LENGTH values n)
if(NOT n EQUAL 4)
message(FATAL_ERROR "port: ${arr} not found in ${mt_eterpack_ref}")
endif()
list(JOIN values ", " values)
string(APPEND mt_eterpack_keys "static DWORD ${arr}[] = { ${values} };\n")
endforeach()
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${mt_eterpack_ref}")
else()
message(WARNING "port: ${mt_eterpack_ref} not found (set MT_40250_SOURCE); EterPack keys are zero")
string(APPEND mt_eterpack_keys "static DWORD s_adwEterPackKey[] = { 0, 0, 0, 0 };\n"
"static DWORD s_adwEterPackSecurityKey[] = { 0, 0, 0, 0 };\n")
endif()
file(CONFIGURE OUTPUT ${MT_PORT_GENERATED}/EterPackKeys.generated.h
CONTENT "// Generated from the 40250 reference EterPack.cpp by port/CMakeLists.txt; do not commit.\n#pragma once\n${mt_eterpack_keys}")
target_include_directories(port_logic PRIVATE ${MT_PORT_GENERATED})
if(WIN32)
target_link_libraries(port_logic PUBLIC winmm)
endif()
@@ -86,6 +124,10 @@ add_library(port_header_gate OBJECT ${MT_PORT_GATE_SOURCES})
target_include_directories(port_header_gate PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${MT_PORT_SHIMS})
target_link_libraries(port_header_gate PRIVATE mt3p::minilzo mt3p::cryptopp)
target_compile_features(port_header_gate PRIVATE cxx_std_20)
target_compile_definitions(port_header_gate PRIVATE "NANOBEGIN=" "NANOEND=")
if(MINGW)
target_compile_options(port_header_gate PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/common/Win32MinMax.h)
endif()
target_compile_options(port_header_gate PRIVATE ${MT_PORT_WARNING_FLAGS})
add_dependencies(port_logic port_header_gate)
@@ -95,4 +137,12 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
add_executable(port_common_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_common_test.cpp)
target_link_libraries(port_common_test PRIVATE port_logic)
add_test(NAME port.common COMMAND $<TARGET_FILE:port_common_test>)
# Against the real 40250 Client/pack next to the reference source; skipped (77) when it is missing,
# failed instead under MT_ASSETS_STRICT=1.
set(MT_40250_CLIENT "${MT_40250_SOURCE}/../../Client" CACHE PATH "40250 Client directory (pack/, Eternexus/)")
add_executable(port_eterpack_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_eterpack_test.cpp)
target_link_libraries(port_eterpack_test PRIVATE port_platform)
add_test(NAME port.eterpack COMMAND $<TARGET_FILE:port_eterpack_test> ${MT_40250_CLIENT})
set_tests_properties(port.eterpack PROPERTIES SKIP_RETURN_CODE 77)
endif()
+49
View File
@@ -0,0 +1,49 @@
#include "StdAfx.h"
#include "Random.h" // PORT: the POSIX-safe names (Random.h) must be in scope for the definitions
#include <assert.h>
static unsigned long randseed = 1;
void srandom(unsigned long seed)
{
randseed = seed;
}
/*
* Pseudo-random number generator for randomizing the profiling clock,
* and whatever else we might use it for. The result is uniform on
* [0, 2^31 - 1].
*/
unsigned long random()
{
long x, hi, lo, t;
/*
* Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
* From "Random number generators: good ones are hard to find",
* Park and Miller, Communications of the ACM, vol. 31, no. 10,
* October 1988, p. 1195.
*/
x = randseed;
hi = x / 127773;
lo = x % 127773;
t = 16807 * lo - 2836 * hi;
if (t <= 0)
t += 0x7fffffff;
randseed = t;
return (t);
}
float frandom(float flLow, float flHigh)
{
float fl = float(random()) / float(2147483648.0f); // float in [0,1)
return (fl * (flHigh - flLow)) + flLow; // float in [low,high)
}
long random_range(long from, long to)
{
assert(from <= to);
return ((random() % (to - from + 1)) + from);
}
+92
View File
@@ -0,0 +1,92 @@
#include "StdAfx.h"
#include "Stl.h"
static std::list<std::string> s_stList;
char korean_tolower(const char c)
{
char ret = c;
if (c >= 'A' && c <= 'Z')
ret = c - 'A' + 'a';
assert(ret == tolower(c));
return ret;
}
std::string& stl_static_string(const char * c_sz)
{
std::string str;
str.assign(c_sz);
s_stList.push_back(str);
return s_stList.back();
}
void stl_lowers(std::string& rstRet)
{
for (size_t i = 0; i < rstRet.length(); ++i)
rstRet[i] = korean_tolower(rstRet[i]);
}
int split_string(const std::string& input, const std::string& delimiter, std::vector<std::string>& results, bool includeEmpties)
{
int iPos = 0;
int newPos = -1;
UINT sizeS2 = delimiter.size();
UINT isize = input.size();
if ((isize == 0) || (sizeS2 == 0))
{
return 0;
}
std::vector<int> positions;
newPos = input.find(delimiter, 0);
if (newPos < 0)
return 0;
int numFound = 0;
while (newPos >= iPos)
{
numFound++;
positions.push_back(newPos);
iPos = newPos;
newPos = input.find(delimiter, iPos+sizeS2);
}
if (numFound == 0)
return 0;
for (UINT i = 0; i <= positions.size(); ++i)
{
std::string s("");
if (i == 0)
{
s = input.substr(i, positions[i]);
}
else
{
UINT offset = positions[i-1] + sizeS2;
if (offset < isize)
{
if (i == positions.size())
{
s = input.substr(offset);
}
else if (i > 0)
{
s = input.substr(positions[i-1] + sizeS2, positions[i] - positions[i-1] - sizeS2);
}
}
}
if (includeEmpties || (s.size() > 0))
{
results.push_back(s);
}
}
return numFound;
}
+156
View File
@@ -0,0 +1,156 @@
#include "StdAfx.h"
#include "Timer.h"
static LARGE_INTEGER gs_liTickCountPerSec;
static DWORD gs_dwBaseTime=0;
static DWORD gs_dwServerTime=0;
static DWORD gs_dwClientTime=0;
static DWORD gs_dwFrameTime=0;
#pragma comment(lib, "winmm.lib")
BOOL ELTimer_Init()
{
/*
gs_liTickCountPerSec.QuadPart=0;
if (!QueryPerformanceFrequency(&gs_liTickCountPerSec))
return 0;
LARGE_INTEGER liTickCount;
QueryPerformanceCounter(&liTickCount);
gs_dwBaseTime= (liTickCount.QuadPart*1000 / gs_liTickCountPerSec.QuadPart);
*/
gs_dwBaseTime = timeGetTime();
return 1;
}
DWORD ELTimer_GetMSec()
{
//assert(gs_dwBaseTime!=0 && "ELTimer_Init 를 먼저 실행하세요");
//LARGE_INTEGER liTickCount;
//QueryPerformanceCounter(&liTickCount);
return timeGetTime() - gs_dwBaseTime; //(liTickCount.QuadPart*1000 / gs_liTickCountPerSec.QuadPart)-gs_dwBaseTime;
}
VOID ELTimer_SetServerMSec(DWORD dwServerTime)
{
NANOBEGIN
if (0 != dwServerTime) // nanomite를 위한 더미 if
{
gs_dwServerTime = dwServerTime;
gs_dwClientTime = CTimer::instance().GetCurrentMillisecond();
}
NANOEND
}
DWORD ELTimer_GetServerMSec()
{
return CTimer::instance().GetCurrentMillisecond() - gs_dwClientTime + gs_dwServerTime;
//return ELTimer_GetMSec() - gs_dwClientTime + gs_dwServerTime;
}
DWORD ELTimer_GetFrameMSec()
{
return gs_dwFrameTime;
}
DWORD ELTimer_GetServerFrameMSec()
{
return ELTimer_GetFrameMSec() - gs_dwClientTime + gs_dwServerTime;
}
VOID ELTimer_SetFrameMSec()
{
gs_dwFrameTime = ELTimer_GetMSec();
}
CTimer::CTimer()
{
ELTimer_Init();
NANOBEGIN
if (this) // nanomite를 위한 더미 if
{
m_dwCurrentTime = 0;
m_bUseRealTime = true;
m_index = 0;
m_dwElapsedTime = 0;
m_fCurrentTime = 0.0f;
}
NANOEND
}
CTimer::~CTimer()
{
}
void CTimer::SetBaseTime()
{
m_dwCurrentTime = 0;
}
void CTimer::Advance()
{
if (!m_bUseRealTime)
{
++m_index;
if (m_index == 1)
m_index = -1;
m_dwCurrentTime += 16 + (m_index & 1);
m_fCurrentTime = m_dwCurrentTime / 1000.0f;
}
else
{
DWORD currentTime = ELTimer_GetMSec();
if (m_dwCurrentTime == 0)
m_dwCurrentTime = currentTime;
m_dwElapsedTime = currentTime - m_dwCurrentTime;
m_dwCurrentTime = currentTime;
}
}
void CTimer::Adjust(int iTimeGap)
{
m_dwCurrentTime += iTimeGap;
}
float CTimer::GetCurrentSecond()
{
if (m_bUseRealTime)
return ELTimer_GetMSec() / 1000.0f;
return m_fCurrentTime;
}
DWORD CTimer::GetCurrentMillisecond()
{
if (m_bUseRealTime)
return ELTimer_GetMSec();
return m_dwCurrentTime;
}
float CTimer::GetElapsedSecond()
{
return GetElapsedMilliecond() / 1000.0f;
}
DWORD CTimer::GetElapsedMilliecond()
{
if (!m_bUseRealTime)
return 16 + (m_index & 1);
return m_dwElapsedTime;
}
void CTimer::UseCustomTime()
{
m_bUseRealTime = false;
}
+411
View File
@@ -0,0 +1,411 @@
#include "StdAfx.h"
#include <stdlib.h>
#include <lzo/lzoLibLink.h>
#include "lzo.h"
#include "tea.h"
#include "Debug.h"
#define dbg_printf
static class LZOFreeMemoryMgr
{
public:
enum
{
REUSING_CAPACITY = 64*1024,
};
public:
~LZOFreeMemoryMgr()
{
std::vector<BYTE*>::iterator i;
for (i = m_freeVector.begin(); i != m_freeVector.end(); ++i)
delete *i;
m_freeVector.clear();
}
BYTE* Alloc(unsigned capacity)
{
assert(capacity > 0);
if (capacity < REUSING_CAPACITY)
{
if (!m_freeVector.empty())
{
BYTE* freeMem = m_freeVector.back();
m_freeVector.pop_back();
dbg_printf("lzo.reuse_alloc\t%p(%d) free\n", freeMem, capacity);
return freeMem;
}
BYTE* newMem = new BYTE[REUSING_CAPACITY];
dbg_printf("lzo.reuse_alloc\t%p(%d) real\n", newMem, capacity);
return newMem;
}
BYTE* newMem = new BYTE[capacity];
dbg_printf("lzo.real_alloc\t%p(%d)\n", newMem, capacity);
return newMem;
}
void Free(BYTE* ptr, unsigned capacity)
{
assert(ptr != NULL);
assert(capacity > 0);
if (capacity < REUSING_CAPACITY)
{
dbg_printf("lzo.reuse_free\t%p(%d)\n", ptr, capacity);
m_freeVector.push_back(ptr);
return;
}
dbg_printf("lzo.real_free\t%p(%d)\n", ptr, capacity);
delete [] ptr;
}
private:
std::vector<BYTE*> m_freeVector;
} gs_freeMemMgr;
DWORD CLZObject::ms_dwFourCC = MAKEFOURCC('M', 'C', 'O', 'Z');
CLZObject::CLZObject()
{
Initialize();
}
void CLZObject::Initialize()
{
m_bInBuffer = false;
m_pbBuffer = NULL;
m_dwBufferSize = 0;
m_pHeader = NULL;
m_pbIn = NULL;
m_bCompressed = false;
}
void CLZObject::Clear()
{
if (m_pbBuffer && !m_bInBuffer)
gs_freeMemMgr.Free(m_pbBuffer, m_dwBufferSize);
if (m_dwBufferSize > 0)
{
dbg_printf("lzo.free %d\n", m_dwBufferSize);
}
Initialize();
}
CLZObject::~CLZObject()
{
Clear();
}
DWORD CLZObject::GetSize()
{
assert(m_pHeader);
if (m_bCompressed)
{
if (m_pHeader->dwEncryptSize)
return sizeof(THeader) + sizeof(DWORD) + m_pHeader->dwEncryptSize;
else
return sizeof(THeader) + sizeof(DWORD) + m_pHeader->dwCompressedSize;
}
else
return m_pHeader->dwRealSize;
}
void CLZObject::BeginCompress(const void * pvIn, UINT uiInLen)
{
m_pbIn = (const BYTE *) pvIn;
// sizeof(SHeader) +
// 암호화를 위한 fourCC 4바이트
// 압축된 후 만들어질 수 있는 최대 용량 +
// 암호화를 위한 8 바이트
m_dwBufferSize = sizeof(THeader) + sizeof(DWORD) + (uiInLen + uiInLen / 64 + 16 + 3) + 8;
m_pbBuffer = gs_freeMemMgr.Alloc(m_dwBufferSize);
memset(m_pbBuffer, 0, m_dwBufferSize);
m_pHeader = (THeader *) m_pbBuffer;
m_pHeader->dwFourCC = ms_dwFourCC;
m_pHeader->dwEncryptSize = m_pHeader->dwCompressedSize = m_pHeader->dwRealSize = 0;
m_pHeader->dwRealSize = uiInLen;
}
void CLZObject::BeginCompressInBuffer(const void * pvIn, UINT uiInLen, void * /*pvOut*/)
{
m_pbIn = (const BYTE *) pvIn;
// sizeof(SHeader) +
// 암호화를 위한 fourCC 4바이트
// 압축된 후 만들어질 수 있는 최대 용량 +
// 암호화를 위한 8 바이트
m_dwBufferSize = sizeof(THeader) + sizeof(DWORD) + (uiInLen + uiInLen / 64 + 16 + 3) + 8;
m_pbBuffer = gs_freeMemMgr.Alloc(m_dwBufferSize);
memset(m_pbBuffer, 0, m_dwBufferSize);
m_pHeader = (THeader *) m_pbBuffer;
m_pHeader->dwFourCC = ms_dwFourCC;
m_pHeader->dwEncryptSize = m_pHeader->dwCompressedSize = m_pHeader->dwRealSize = 0;
m_pHeader->dwRealSize = uiInLen;
m_bInBuffer = true;
}
bool CLZObject::Compress()
{
UINT iOutLen;
BYTE * pbBuffer;
pbBuffer = m_pbBuffer + sizeof(THeader);
*(DWORD *) pbBuffer = ms_dwFourCC;
pbBuffer += sizeof(DWORD);
#if defined( LZO1X_999_MEM_COMPRESS )
int r = lzo1x_999_compress((BYTE *) m_pbIn, m_pHeader->dwRealSize, pbBuffer, (lzo_uint*) &iOutLen, CLZO::Instance().GetWorkMemory());
#else
int r = lzo1x_1_compress((BYTE *) m_pbIn, m_pHeader->dwRealSize, pbBuffer, (lzo_uint*) &iOutLen, CLZO::Instance().GetWorkMemory());
#endif
if (LZO_E_OK != r)
{
TraceError("LZO: lzo1x_999_compress failed");
return false;
}
m_pHeader->dwCompressedSize = iOutLen;
m_bCompressed = true;
return true;
}
bool CLZObject::BeginDecompress(const void * pvIn)
{
THeader * pHeader = (THeader *) pvIn;
if (pHeader->dwFourCC != ms_dwFourCC)
{
TraceError("LZObject: not a valid data");
return false;
}
m_pHeader = pHeader;
m_pbIn = (const BYTE *) pvIn + (sizeof(THeader) + sizeof(DWORD));
/*
static unsigned sum = 0;
static unsigned count = 0;
sum += pHeader->dwRealSize;
count++;
printf("decompress cur: %d, ave: %d\n", pHeader->dwRealSize, sum/count);
*/
m_dwBufferSize = pHeader->dwRealSize;
m_pbBuffer = gs_freeMemMgr.Alloc(m_dwBufferSize);
memset(m_pbBuffer, 0, pHeader->dwRealSize);
return true;
}
class DecryptBuffer
{
public:
enum
{
LOCAL_BUF_SIZE = 8 * 1024,
};
public:
DecryptBuffer(unsigned size)
{
static unsigned count = 0;
static unsigned sum = 0;
static unsigned maxSize = 0;
sum += size;
count++;
maxSize = max(size, maxSize);
if (size >= LOCAL_BUF_SIZE)
{
m_buf = new char[size];
dbg_printf("DecryptBuffer - AllocHeap %d max(%d) ave(%d)\n", size, maxSize/1024, sum/count);
}
else
{
dbg_printf("DecryptBuffer - AllocStack %d max(%d) ave(%d)\n", size, maxSize/1024, sum/count);
m_buf = m_local_buf;
}
}
~DecryptBuffer()
{
if (m_local_buf != m_buf)
{
dbg_printf("DecruptBuffer - FreeHeap\n");
delete [] m_buf;
}
else
{
dbg_printf("DecruptBuffer - FreeStack\n");
}
}
void* GetBufferPtr()
{
return m_buf;
}
private:
char* m_buf;
char m_local_buf[LOCAL_BUF_SIZE];
};
bool CLZObject::Decompress(DWORD * pdwKey)
{
UINT uiSize;
int r;
if (m_pHeader->dwEncryptSize)
{
DecryptBuffer buf(m_pHeader->dwEncryptSize);
BYTE* pbDecryptedBuffer = (BYTE*)buf.GetBufferPtr();
__Decrypt(pdwKey, pbDecryptedBuffer);
if (*(DWORD *) pbDecryptedBuffer != ms_dwFourCC)
{
TraceError("LZObject: key incorrect");
return false;
}
if (LZO_E_OK != (r = lzo1x_decompress(pbDecryptedBuffer + sizeof(DWORD), m_pHeader->dwCompressedSize, m_pbBuffer, (lzo_uint*) &uiSize, NULL)))
{
TraceError("LZObject: Decompress failed(decrypt) ret %d\n", r);
return false;
}
}
else
{
uiSize = m_pHeader->dwRealSize;
//if (LZO_E_OK != (r = lzo1x_decompress_safe(m_pbIn, m_pHeader->dwCompressedSize, m_pbBuffer, (lzo_uint*) &uiSize, NULL)))
if (LZO_E_OK != (r = lzo1x_decompress(m_pbIn, m_pHeader->dwCompressedSize, m_pbBuffer, (lzo_uint*) &uiSize, NULL)))
{
TraceError("LZObject: Decompress failed : ret %d, CompressedSize %d\n", r, m_pHeader->dwCompressedSize);
return false;
}
}
if (uiSize != m_pHeader->dwRealSize)
{
TraceError("LZObject: Size differs");
return false;
}
return true;
}
bool CLZObject::Encrypt(DWORD * pdwKey)
{
if (!m_bCompressed)
{
assert(!"not compressed yet");
return false;
}
BYTE * pbBuffer = m_pbBuffer + sizeof(THeader);
m_pHeader->dwEncryptSize = tea_encrypt((DWORD *) pbBuffer, (const DWORD *) pbBuffer, pdwKey, m_pHeader->dwCompressedSize + 19);
return true;
}
bool CLZObject::__Decrypt(DWORD * key, BYTE* data)
{
assert(m_pbBuffer);
tea_decrypt((DWORD *) data, (const DWORD *) (m_pbIn - sizeof(DWORD)), key, m_pHeader->dwEncryptSize);
return true;
}
void CLZObject::AllocBuffer(DWORD dwSrcSize)
{
if (m_pbBuffer && !m_bInBuffer)
gs_freeMemMgr.Free(m_pbBuffer, m_dwBufferSize);
m_pbBuffer = gs_freeMemMgr.Alloc(dwSrcSize);
m_dwBufferSize = dwSrcSize;
}
/*
void CLZObject::CopyBuffer(const char* pbSrc, DWORD dwSrcSize)
{
AllocBuffer(dwSrcSize);
memcpy(m_pbBuffer, pbSrc, dwSrcSize);
}
*/
CLZO::CLZO() : m_pWorkMem(NULL)
{
if (lzo_init() != LZO_E_OK)
{
TraceError("LZO: cannot initialize");
return;
}
#if defined( LZO1X_999_MEM_COMPRESS )
m_pWorkMem = (BYTE *) malloc(LZO1X_999_MEM_COMPRESS);
#else
m_pWorkMem = (BYTE *) malloc(LZO1X_1_MEM_COMPRESS);
#endif
if (NULL == m_pWorkMem)
{
TraceError("LZO: cannot alloc memory");
return;
}
}
CLZO::~CLZO()
{
if (m_pWorkMem)
{
free(m_pWorkMem);
m_pWorkMem = NULL;
}
}
bool CLZO::CompressMemory(CLZObject & rObj, const void * pIn, UINT uiInLen)
{
rObj.BeginCompress(pIn, uiInLen);
return rObj.Compress();
}
bool CLZO::CompressEncryptedMemory(CLZObject & rObj, const void * pIn, UINT uiInLen, DWORD * pdwKey)
{
rObj.BeginCompress(pIn, uiInLen);
if (rObj.Compress())
{
if (rObj.Encrypt(pdwKey))
return true;
return false;
}
return false;
}
bool CLZO::Decompress(CLZObject & rObj, const BYTE * pbBuf, DWORD * pdwKey)
{
if (!rObj.BeginDecompress(pbBuf))
return false;
if (!rObj.Decompress(pdwKey))
return false;
return true;
}
BYTE * CLZO::GetWorkMemory()
{
return m_pWorkMem;
}
+101
View File
@@ -0,0 +1,101 @@
/*
* Filename: tea.c
* Description: TEA 암호화 모듈
*
* Author: 김한주 (aka. 비엽, Cronan), 송영진 (aka. myevan, 빗자루)
*/
#include "StdAfx.h"
#include "tea.h"
#include <memory.h>
/*
* TEA Encryption Module Instruction
* Edited by 김한주 aka. 비엽, Cronan
*
* void tea_code(const unsigned long sz, const unsigned long sy, const unsigned long *key, unsigned long *dest)
* void tea_decode(const unsigned long sz, const unsigned long sy, const unsigned long *key, unsigned long *dest)
* 8바이트를 암호/복호화 할때 사용된다. key 는 16 바이트여야 한다.
* sz, sy 는 8바이트의 역순으로 대입한다.
*
* int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
* int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
* 한꺼번에 8 바이트 이상을 암호/복호화 할때 사용한다. 만약 size 가
* 8의 배수가 아니면 8의 배수로 크기를 "늘려서" 암호화 한다.
*
* ex. tea_code(pdwSrc[1], pdwSrc[0], pdwKey, pdwDest);
* tea_decrypt(pdwDest, pdwSrc, pdwKey, nSize);
*/
#define TEA_ROUND 32 // 32 를 권장하며, 높을 수록 결과가 난해해 진다.
#define DELTA 0x9E3779B9 // DELTA 값 바꾸지 말것.
void tea_code(const tea_word sz, const tea_word sy, const tea_word *key, tea_word *dest) // PORT: tea_word (32-bit Win32 unsigned long)
{
tea_word y = sy, z = sz, sum = 0; // PORT: tea_word (32-bit Win32 unsigned long)
tea_word n = TEA_ROUND; // PORT: tea_word (32-bit Win32 unsigned long)
while (n-- > 0)
{
y += ((z << 4 ^ z >> 5) + z) ^ (sum + key[sum & 3]);
sum += DELTA;
z += ((y << 4 ^ y >> 5) + y) ^ (sum + key[sum >> 11 & 3]);
}
*(dest++) = y;
*dest = z;
}
void tea_decode(const tea_word sz, const tea_word sy, const tea_word *key, tea_word *dest) // PORT: tea_word (32-bit Win32 unsigned long)
{
#pragma warning(disable:4307)
tea_word y = sy, z = sz, sum = DELTA * TEA_ROUND; // PORT: tea_word (32-bit Win32 unsigned long)
#pragma warning(default:4307)
tea_word n = TEA_ROUND; // PORT: tea_word (32-bit Win32 unsigned long)
while (n-- > 0)
{
z -= ((y << 4 ^ y >> 5) + y) ^ (sum + key[sum >> 11 & 3]);
sum -= DELTA;
y -= ((z << 4 ^ z >> 5) + z) ^ (sum + key[sum & 3]);
}
*(dest++) = y;
*dest = z;
}
int tea_encrypt(tea_word *dest, const tea_word *src, const tea_word * key, int size) // PORT: tea_word (32-bit Win32 unsigned long)
{
int i;
int resize;
if (size % 8 != 0)
{
resize = size + 8 - (size % 8);
memset((char *) src + size, 0, resize - size);
}
else
resize = size;
for (i = 0; i < resize >> 3; i++, dest += 2, src += 2)
tea_code(*(src + 1), *src, key, dest);
return (resize);
}
int tea_decrypt(tea_word *dest, const tea_word *src, const tea_word * key, int size) // PORT: tea_word (32-bit Win32 unsigned long)
{
int i;
int resize;
if (size % 8 != 0)
resize = size + 8 - (size % 8);
else
resize = size;
for (i = 0; i < resize >> 3; i++, dest += 2, src += 2)
tea_decode(*(src + 1), *src, key, dest);
return (resize);
}
+10 -2
View File
@@ -1,3 +1,11 @@
// PORT: the 32-bit Win32 unsigned long; on Windows it stays unsigned long so DWORD* callers match.
#if defined(_WIN32)
typedef unsigned long tea_word;
#else
#include <stdint.h>
typedef uint32_t tea_word;
#endif
#ifdef __cplusplus
extern "C" {
#endif
@@ -11,8 +19,8 @@ extern "C" {
#define TEA_KEY_LENGTH 16
int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
int tea_encrypt(tea_word *dest, const tea_word *src, const tea_word *key, int size); // PORT: tea_word (32-bit)
int tea_decrypt(tea_word *dest, const tea_word *src, const tea_word *key, int size); // PORT: tea_word (32-bit)
#ifdef __cplusplus
};
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -23,7 +23,7 @@ namespace eterpack
const DWORD c_IndexCC = MAKEFOURCC('E', 'P', 'K', 'D');
const DWORD c_Version = 2;
// FourCC + Version + m_indexCount
const DWORD c_HeaderSize = sizeof(DWORD) + sizeof(DWORD) + sizeof(long);
const DWORD c_HeaderSize = sizeof(DWORD) + sizeof(DWORD) + sizeof(LONG); // PORT: LONG (32-bit) for Win32 long
};
enum EEterPackTypes
@@ -46,20 +46,21 @@ enum EEterPackTypes
#pragma pack(push, 4)
typedef struct SEterPackIndex
{
long id;
LONG id; // PORT: fixed-width Win32 long
char filename[FILENAME_MAX_LEN + 1];
DWORD filename_crc;
long real_data_size;
long data_size;
LONG real_data_size; // PORT: fixed-width Win32 long
LONG data_size; // PORT: fixed-width Win32 long
#ifdef CHECKSUM_CHECK_MD5
BYTE MD5Digest[16];
#else
DWORD data_crc;
#endif
long data_position;
LONG data_position; // PORT: fixed-width Win32 long
char compressed_type;
} TEterPackIndex;
#pragma pack(pop)
static_assert(sizeof(TEterPackIndex) == 192, "TEterPackIndex on-disk layout"); // PORT: on-disk struct
typedef std::unordered_map<DWORD, TEterPackIndex *> TDataPositionMap;
typedef std::list<TEterPackIndex *> TFreeIndexList;
@@ -170,7 +171,7 @@ class CEterPack
char* m_file_data;
unsigned m_file_size;
long m_indexCount;
LONG m_indexCount; // PORT: read as 32-bit from the index header
bool m_bEncrypted;
char m_dbName[DBNAME_MAX_LEN+1];
@@ -0,0 +1,59 @@
#include "StdAfx.h"
#include "EterPackCursor.h"
#include "Inline.h"
CEterPackCursor::CEterPackCursor(CEterPack* pack) : m_pPack(pack), m_pData(NULL), m_ReadPoint(0)
{
}
CEterPackCursor::~CEterPackCursor()
{
Close();
}
bool CEterPackCursor::Open(const char* filename)
{
assert(m_pPack != NULL);
char tmpFilename[MAX_PATH + 1];
strncpy(tmpFilename, filename, MAX_PATH);
inlineConvertPackFilename(tmpFilename);
if (!m_pPack->Get(m_file, tmpFilename, &m_pData))
return false;
return true;
}
void CEterPackCursor::Close()
{
m_file.Destroy();
m_pData = NULL;
m_ReadPoint = 0;
}
void CEterPackCursor::Seek(long offset)
{
m_ReadPoint = max(0, min(Size(), offset));
}
bool CEterPackCursor::Read(LPVOID data, long size)
{
if (m_file.IsNull())
return false;
if (m_ReadPoint + size > Size())
return false;
memcpy(data, (char*) m_pData + m_ReadPoint, size);
m_ReadPoint += size;
return true;
}
long CEterPackCursor::Size()
{
if (m_file.IsNull())
return 0;
return m_file.Size();
}
@@ -0,0 +1,25 @@
#ifndef __INC_ETERPACKCURSOR_H__
#define __INC_ETERPACKCURSOR_H__
#include "EterPack.h"
class CEterPackCursor
{
public:
CEterPackCursor(CEterPack * pack);
~CEterPackCursor();
bool Open(const char* filename);
void Close();
void Seek(long offset);
bool Read(LPVOID data, long size);
long Size();
private:
CEterPack * m_pPack;
CMappedFile m_file;
LPCVOID m_pData;
long m_ReadPoint;
};
#endif
@@ -0,0 +1,621 @@
#include "StdAfx.h"
#include <io.h>
#include <assert.h>
#include "EterPackManager.h"
#include "EterPackPolicy_CSHybridCrypt.h"
#include "../EterBase/Debug.h"
#include "../EterBase/CRC32.h"
#define PATH_ABSOLUTE_YMIRWORK1 "d:/ymir work/"
#define PATH_ABSOLUTE_YMIRWORK2 "d:\\ymir work\\"
#ifdef __THEMIDA__
#include <ThemidaSDK.h>
#endif
CEterPack* CEterPackManager::FindPack(const char* c_szPathName)
{
std::string strFileName;
if (0 == ConvertFileName(c_szPathName, strFileName))
{
return &m_RootPack;
}
else
{
for (TEterPackMap::iterator itor = m_DirPackMap.begin(); itor != m_DirPackMap.end(); ++itor)
{
const std::string & c_rstrName = itor->first;
CEterPack * pEterPack = itor->second;
if (CompareName(c_rstrName.c_str(), c_rstrName.length(), strFileName.c_str()))
{
return pEterPack;
}
}
}
return NULL;
}
void CEterPackManager::SetCacheMode()
{
m_isCacheMode=true;
}
void CEterPackManager::SetRelativePathMode()
{
m_bTryRelativePath = true;
}
// StringPath std::string 버전
int CEterPackManager::ConvertFileName(const char * c_szFileName, std::string & rstrFileName)
{
rstrFileName = c_szFileName;
stl_lowers(rstrFileName);
int iCount = 0;
for (DWORD i = 0; i < rstrFileName.length(); ++i)
{
if (rstrFileName[i] == '/')
++iCount;
else if (rstrFileName[i] == '\\')
{
rstrFileName[i] = '/';
++iCount;
}
}
return iCount;
}
bool CEterPackManager::CompareName(const char * c_szDirectoryName, DWORD /*dwLength*/, const char * c_szFileName)
{
const char * c_pszSrc = c_szDirectoryName;
const char * c_pszCmp = c_szFileName;
while (*c_pszSrc)
{
if (*(c_pszSrc++) != *(c_pszCmp++))
return false;
if (!*c_pszCmp)
return false;
}
return true;
}
void CEterPackManager::LoadStaticCache(const char* c_szFileName)
{
if (!m_isCacheMode)
return;
std::string strFileName;
if (0 == ConvertFileName(c_szFileName, strFileName))
{
return;
}
DWORD dwFileNameHash = GetCRC32(strFileName.c_str(), strFileName.length());
std::unordered_map<DWORD, SCache>::iterator f = m_kMap_dwNameKey_kCache.find(dwFileNameHash);
if (m_kMap_dwNameKey_kCache.end() != f)
return;
CMappedFile kMapFile;
const void* c_pvData;
if (!Get(kMapFile, c_szFileName, &c_pvData))
return;
SCache kNewCache;
kNewCache.m_dwBufSize = kMapFile.Size();
kNewCache.m_abBufData = new BYTE[kNewCache.m_dwBufSize];
memcpy(kNewCache.m_abBufData, c_pvData, kNewCache.m_dwBufSize);
m_kMap_dwNameKey_kCache.insert(std::unordered_map<DWORD, SCache>::value_type(dwFileNameHash, kNewCache));
}
CEterPackManager::SCache* CEterPackManager::__FindCache(DWORD dwFileNameHash)
{
std::unordered_map<DWORD, SCache>::iterator f=m_kMap_dwNameKey_kCache.find(dwFileNameHash);
if (m_kMap_dwNameKey_kCache.end()==f)
return NULL;
return &f->second;
}
void CEterPackManager::__ClearCacheMap()
{
std::unordered_map<DWORD, SCache>::iterator i;
for (i = m_kMap_dwNameKey_kCache.begin(); i != m_kMap_dwNameKey_kCache.end(); ++i)
delete [] i->second.m_abBufData;
m_kMap_dwNameKey_kCache.clear();
}
struct TimeChecker
{
TimeChecker(const char* name) : name(name)
{
baseTime = timeGetTime();
}
~TimeChecker()
{
printf("load %s (%d)\n", name, timeGetTime() - baseTime);
}
const char* name;
DWORD baseTime;
};
bool CEterPackManager::Get(CMappedFile & rMappedFile, const char * c_szFileName, LPCVOID * pData)
{
//TimeChecker timeChecker(c_szFileName);
//Logf(1, "Load %s\n", c_szFileName);
if (m_iSearchMode == SEARCH_FILE_FIRST)
{
if (GetFromFile(rMappedFile, c_szFileName, pData))
{
return true;
}
return GetFromPack(rMappedFile, c_szFileName, pData);
}
if (GetFromPack(rMappedFile, c_szFileName, pData))
{
return true;
}
return GetFromFile(rMappedFile, c_szFileName, pData);
}
struct FinderLock
{
FinderLock(CRITICAL_SECTION& cs) : p_cs(&cs)
{
EnterCriticalSection(p_cs);
}
~FinderLock()
{
LeaveCriticalSection(p_cs);
}
CRITICAL_SECTION* p_cs;
};
bool CEterPackManager::GetFromPack(CMappedFile & rMappedFile, const char * c_szFileName, LPCVOID * pData)
{
FinderLock lock(m_csFinder);
static std::string strFileName;
if (0 == ConvertFileName(c_szFileName, strFileName))
{
return m_RootPack.Get(rMappedFile, strFileName.c_str(), pData);
}
else
{
DWORD dwFileNameHash = GetCRC32(strFileName.c_str(), strFileName.length());
SCache* pkCache = __FindCache(dwFileNameHash);
if (pkCache)
{
rMappedFile.Link(pkCache->m_dwBufSize, pkCache->m_abBufData);
return true;
}
CEterFileDict::Item* pkFileItem = m_FileDict.GetItem(dwFileNameHash, strFileName.c_str());
if (pkFileItem)
if (pkFileItem->pkPack)
{
bool r = pkFileItem->pkPack->Get2(rMappedFile, strFileName.c_str(), pkFileItem->pkInfo, pData);
//pkFileItem->pkPack->ClearDataMemoryMap();
return r;
}
}
#ifdef _DEBUG
TraceError("CANNOT_FIND_PACK_FILE [%s]", strFileName.c_str());
#endif
return false;
}
const time_t g_tCachingInterval = 10; // 10초
void CEterPackManager::ArrangeMemoryMappedPack()
{
//time_t curTime = time(NULL);
//CEterFileDict::TDict dict = m_FileDict.GetDict();
//for (CEterFileDict::TDict::iterator it = dict.begin(); it != dict.end(); ++it)
//{
// CEterFileDict::Item &rFileItem = it->second;
// CEterPack* pkPack = rFileItem.pkPack;
// if (pkPack)
// {
// if (curTime - pkPack->GetLastAccessTime() > g_tCachingInterval)
// {
// pkPack->ClearDataMemoryMap();
// }
// }
//}
}
bool CEterPackManager::GetFromFile(CMappedFile & rMappedFile, const char * c_szFileName, LPCVOID * pData)
{
#ifndef _DEBUG
//const char *pcExt = strchr(c_szFileName, '.');
//if (pcExt &&
// _strnicmp(pcExt, ".py", 3) == 0 && // python 스크립트 중
// stricmp(c_szFileName, "logininfo.py") != 0 && // 로그인 정보 파일이 아니고
// strnicmp(c_szFileName, "locale", 6) != 0
// )
//{
// return false;
//}
#endif
//if(m_bTryRelativePath) {
// if (strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK1, strlen(PATH_ABSOLUTE_YMIRWORK1)) == 0 || strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK2, strlen(PATH_ABSOLUTE_YMIRWORK2)) == 0) {
// if(rMappedFile.Create(c_szFileName+strlen(PATH_ABSOLUTE_YMIRWORK1), pData, 0, 0))
// {
// return true;
// }
// }
//}
return rMappedFile.Create(c_szFileName, pData, 0, 0) ? true : false;
}
bool CEterPackManager::isExistInPack(const char * c_szFileName)
{
std::string strFileName;
if (0 == ConvertFileName(c_szFileName, strFileName))
{
return m_RootPack.IsExist(strFileName.c_str());
}
else
{
DWORD dwFileNameHash = GetCRC32(strFileName.c_str(), strFileName.length());
CEterFileDict::Item* pkFileItem = m_FileDict.GetItem(dwFileNameHash, strFileName.c_str());
if (pkFileItem)
if (pkFileItem->pkPack)
return pkFileItem->pkPack->IsExist(strFileName.c_str());
}
// NOTE : 매치 되는 팩이 없다면 false - [levites]
return false;
}
bool CEterPackManager::isExist(const char * c_szFileName)
{
if (m_iSearchMode == SEARCH_PACK_FIRST)
{
if (isExistInPack(c_szFileName))
return true;
return _access(c_szFileName, 0) == 0 ? true : false;
}
//if(m_bTryRelativePath) {
// if (strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK1, strlen(PATH_ABSOLUTE_YMIRWORK1)) == 0 || strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK2, strlen(PATH_ABSOLUTE_YMIRWORK2)) == 0) {
// if(access(c_szFileName+strlen(PATH_ABSOLUTE_YMIRWORK1), 0) == 0)
// return true;
// }
//}
if (_access(c_szFileName, 0) == 0)
return true;
return isExistInPack(c_szFileName);
}
void CEterPackManager::RegisterRootPack(const char * c_szName)
{
if (!m_RootPack.Create(m_FileDict, c_szName, ""))
{
TraceError("%s: Pack file does not exist", c_szName);
}
}
const char * CEterPackManager::GetRootPackFileName()
{
return m_RootPack.GetDBName();
}
bool CEterPackManager::DecryptPackIV(DWORD dwPanamaKey)
{
TEterPackMap::iterator itor = m_PackMap.begin();
while (itor != m_PackMap.end())
{
itor->second->DecryptIV(dwPanamaKey);
itor++;
}
return true;
}
bool CEterPackManager::RegisterPackWhenPackMaking(const char * c_szName, const char * c_szDirectory, CEterPack* pPack)
{
m_PackMap.insert(TEterPackMap::value_type(c_szName, pPack));
m_PackList.push_front(pPack);
m_DirPackMap.insert(TEterPackMap::value_type(c_szDirectory, pPack));
return true;
}
bool CEterPackManager::RegisterPack(const char * c_szName, const char * c_szDirectory, const BYTE* c_pbIV)
{
CEterPack * pEterPack = NULL;
{
TEterPackMap::iterator itor = m_PackMap.find(c_szName);
if (m_PackMap.end() == itor)
{
bool bReadOnly = true;
pEterPack = new CEterPack;
if (pEterPack->Create(m_FileDict, c_szName, c_szDirectory, bReadOnly, c_pbIV))
{
m_PackMap.insert(TEterPackMap::value_type(c_szName, pEterPack));
}
else
{
#ifdef _DEBUG
Tracef("The eterpack doesn't exist [%s]\n", c_szName);
#endif
delete pEterPack;
pEterPack = NULL;
return false;
}
}
else
{
pEterPack = itor->second;
}
}
if (c_szDirectory && c_szDirectory[0] != '*')
{
TEterPackMap::iterator itor = m_DirPackMap.find(c_szDirectory);
if (m_DirPackMap.end() == itor)
{
m_PackList.push_front(pEterPack);
m_DirPackMap.insert(TEterPackMap::value_type(c_szDirectory, pEterPack));
}
}
return true;
}
void CEterPackManager::SetSearchMode(bool bPackFirst)
{
m_iSearchMode = bPackFirst ? SEARCH_PACK_FIRST : SEARCH_FILE_FIRST;
}
int CEterPackManager::GetSearchMode()
{
return m_iSearchMode;
}
CEterPackManager::CEterPackManager() : m_bTryRelativePath(false), m_iSearchMode(SEARCH_FILE_FIRST), m_isCacheMode(false)
{
InitializeCriticalSection(&m_csFinder);
}
CEterPackManager::~CEterPackManager()
{
__ClearCacheMap();
TEterPackMap::iterator i = m_PackMap.begin();
TEterPackMap::iterator e = m_PackMap.end();
while (i != e)
{
delete i->second;
i++;
}
DeleteCriticalSection(&m_csFinder);
}
void CEterPackManager::RetrieveHybridCryptPackKeys(const BYTE *pStream)
{
////dump file format
//total packagecnt (4byte)
// for packagecntpackage
// db name hash ( stl.h stringhash )
// extension cnt( 4byte)
// for extension cnt
// ext hash ( stl.h stringhash )
// key-16byte
// iv-16byte
int iMemOffset = 0;
int iPackageCnt;
DWORD dwPackageNameHash;
memcpy( &iPackageCnt, pStream + iMemOffset, sizeof(int) );
iMemOffset += sizeof(iPackageCnt);
for( int i = 0; i < iPackageCnt; ++i )
{
int iRecvedCryptKeySize = 0;
memcpy( &iRecvedCryptKeySize, pStream + iMemOffset, sizeof(iRecvedCryptKeySize) );
iRecvedCryptKeySize -= sizeof(dwPackageNameHash); // 서버에서 받은 key stream에는 filename hash가 포함되어 있으므로, hash 사이즈 만큼 배줌.
iMemOffset += sizeof(iRecvedCryptKeySize);
memcpy( &dwPackageNameHash, pStream + iMemOffset, sizeof(dwPackageNameHash) );
iMemOffset += sizeof(dwPackageNameHash);
TEterPackMap::const_iterator cit;
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
auto ssvv = std::string(cit->first);
std::string noPathName = CFileNameHelper::NoPath(ssvv);
if( dwPackageNameHash == stringhash().GetHash(noPathName) )
{
EterPackPolicy_CSHybridCrypt* pCryptPolicy = cit->second->GetPackPolicy_HybridCrypt();
int iHavedCryptKeySize = pCryptPolicy->ReadCryptKeyInfoFromStream( pStream + iMemOffset );
if (iRecvedCryptKeySize != iHavedCryptKeySize)
{
TraceError("CEterPackManager::RetrieveHybridCryptPackKeys cryptokey length of file(%s) is not matched. received(%d) != haved(%d)", noPathName.c_str(), iRecvedCryptKeySize, iHavedCryptKeySize);
}
break;
}
}
iMemOffset += iRecvedCryptKeySize;
}
}
void CEterPackManager::RetrieveHybridCryptPackSDB( const BYTE* pStream )
{
//cnt
//for cnt
//DWORD dwPackageIdentifier;
//DWORD dwFileIdentifier;
//std::vector<BYTE> vecSDBStream;
int iReadOffset = 0;
int iSDBInfoCount = 0;
memcpy( &iSDBInfoCount, pStream+iReadOffset, sizeof(int) );
iReadOffset += sizeof(int);
for( int i = 0; i < iSDBInfoCount; ++i )
{
DWORD dwPackgeIdentifier;
memcpy( &dwPackgeIdentifier, pStream+iReadOffset, sizeof(DWORD) );
iReadOffset += sizeof(DWORD);
TEterPackMap::const_iterator cit;
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
auto ssvv = std::string(cit->first);
std::string noPathName = CFileNameHelper::NoPath(ssvv);
if( dwPackgeIdentifier == stringhash().GetHash(noPathName) )
{
EterPackPolicy_CSHybridCrypt* pCryptPolicy = cit->second->GetPackPolicy_HybridCrypt();
iReadOffset += pCryptPolicy->ReadSupplementatyDataBlockFromStream( pStream+iReadOffset );
break;
}
}
}
}
void CEterPackManager::WriteHybridCryptPackInfo(const char* pFileName)
{
//NOTE : this file format contains a little bit of redundant data.
//however it`s better for seperating cryptkey & supplementary data block.
//dump file format
//SDB data offset(4)
// about cryptkey
//total packagecnt (4byte)
// for packagecnt
// db name hash 4byte( stl.h stringhash )
// extension cnt( 4byte)
// for extension cnt
// ext hash ( stl.h stringhash )
// key-16byte
// iv-16byte
//about SDB data
//total packagecnt (4byte)
// for packagecnt
// db name hash 4byte( stl.h stringhash ) +child node size(4byte)
// sdb file cnt( 4byte )
// for sdb file cnt
// filename hash ( stl.h stringhash )
// related map name size(4), relate map name
// sdb block size( 1byte )
// sdb blocks
CFileBase keyFile;
if( !keyFile.Create( pFileName, CFileBase::FILEMODE_WRITE) )
{
//TODO : write log
return;
}
int iKeyPackageCount = 0;
//write later ( SDB Offset & PackageCnt for Key )
keyFile.SeekCur(2*sizeof(int));
TEterPackMap::const_iterator cit;
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
EterPackPolicy_CSHybridCrypt* pPolicy = cit->second->GetPackPolicy_HybridCrypt();
if( !pPolicy || !pPolicy->IsContainingCryptKey() )
continue;
iKeyPackageCount++;
auto ssvv = std::string(cit->first);
std::string noPathName = CFileNameHelper::NoPath(ssvv);
DWORD dwPackNamehash = stringhash().GetHash(noPathName);
CMakePackLog::GetSingleton().Writef("CEterPackManager::WriteHybridCryptPackInfo PackName : %s, Hash : %x", noPathName.c_str(), dwPackNamehash);
keyFile.Write( &dwPackNamehash, sizeof(DWORD) );
pPolicy->WriteCryptKeyToFile( keyFile );
}
//Write SDB Data
int iSDBDataOffset = keyFile.GetPosition();
int iSDBPackageCnt = 0;
//Write SDB PackageCnt Later
keyFile.SeekCur(sizeof(int));
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
EterPackPolicy_CSHybridCrypt* pPolicy = cit->second->GetPackPolicy_HybridCrypt();
if( !pPolicy || !pPolicy->IsContainingSDBFile() )
continue;
iSDBPackageCnt++;
auto ssvv1 = std::string(cit->first);
auto noPathName = CFileNameHelper::NoPath(ssvv1);
DWORD dwPackNamehash = stringhash().GetHash(noPathName);
keyFile.Write( &dwPackNamehash, sizeof(DWORD) );
int iSDBSizeWriteOffset = keyFile.GetPosition();
keyFile.SeekCur(sizeof(int));
pPolicy->WriteSupplementaryDataBlockToFile( keyFile );
int iSDBSizeAfterWrite = keyFile.GetPosition();
keyFile.Seek(iSDBSizeWriteOffset);
int iSDBSize = iSDBSizeAfterWrite-(iSDBSizeWriteOffset+4);
keyFile.Write( &iSDBSize, sizeof(int) );
keyFile.Seek(iSDBSizeAfterWrite);
}
//write sdb data start offset & package cnt
keyFile.Seek(0);
keyFile.Write( &iSDBDataOffset, sizeof(int));
keyFile.Write( &iKeyPackageCount, sizeof(int));
keyFile.Seek(iSDBDataOffset);
keyFile.Write( &iSDBPackageCnt, sizeof(int));
keyFile.Close();
}
@@ -0,0 +1,503 @@
#include "StdAfx.h"
#include "EterPackPolicy_CSHybridCrypt.h"
#include "../EterBase/Stl.h"
#include "../EterBase/Filename.h"
#include "../EterBase/FileBase.h"
#include "../EterBase/CRC32.h"
#include "../EterBase/lzo.h"
#include "../EterBase/Random.h"
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>
using namespace CryptoPP;
#define CIPHER_MODE CTR_Mode
#ifdef __THEMIDA__
#include <ThemidaSDK.h>
#endif
//Cipher
//Block Size
//Key Length
//
//Default Minimum Maximum
//AES(Rijndael) 16 16 16 32
//Blowfish 8 16 0 56
//Camellia 16 16 16 32
//CAST-128 8 16 5 16
//CAST-256 16 16 16 32
//DES 8 8 8 8
//DES-EDE2 8 16 16 16
//DES-EDE3 8 24 24 24
//DES-XEX3 8 24 24 24
//GOST 8 32 32 32
//IDEA 8 16 16 16
//MARS 16 16 16 56
//RC2 8 16 1 128
//RC5 8 16 0 255
//RC6 16 16 0 255
//SAFER-K 8 16 8 16
//SAFER-SK 8 16 8 16
//Serpent 16 16 1 32
//SHACAL-2 32 16 1 64
//SHARK-E 8 16 1 16
//SKIPJACK 8 10 1 10
//3-Way 12 12 1 12
//Twofish 16 16 0 32
//XTEA 8 16 1 16
inline std::string GetFileExt( std::string& rfileName )
{
stl_lowers(rfileName);
return CFileNameHelper::GetExtension(rfileName);
}
EterPackPolicy_CSHybridCrypt::~EterPackPolicy_CSHybridCrypt()
{
m_mapHybridCryptKey.clear();
m_mapSDBMap.clear();
}
bool EterPackPolicy_CSHybridCrypt::IsContainingCryptKey() const
{
return (m_mapHybridCryptKey.size() > 0) ? true : false;
}
bool EterPackPolicy_CSHybridCrypt::GenerateCryptKey( std::string& rfileName )
{
#ifdef __THEMIDA__
VM_START
#endif
//make lower & extract ext
std::string extName = GetFileExt(rfileName);
stl_lowers(extName);
DWORD dwExtHash = stringhash().GetHash(extName);
TCSHybridCryptKeyMap::const_iterator cit = m_mapHybridCryptKey.find( dwExtHash );
if( cit != m_mapHybridCryptKey.end() )
{
//TODO : log already registered
return false;
}
static AutoSeededRandomPool rnd;
TCSHybridCryptKey info;
{
rnd.GenerateBlock( &(info.uEncryptKey.key[0]), sizeof(info.uEncryptKey) );
rnd.GenerateBlock( &(info.uEncryptIV.iv[0]), sizeof(info.uEncryptIV) );
//for test
/* memset( &info.uEncryptKey.key, 0x10, sizeof(info.uEncryptKey) );
memset( &info.uEncryptIV.iv, 0x10, sizeof(info.uEncryptIV) ); */
}
m_mapHybridCryptKey[dwExtHash] = info;
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::GetPerFileCryptKey( std::string& rfileName, eHybridCipherAlgorithm& eAlgorithm, TEncryptKey& key, TEncryptIV& iv )
{
#ifdef __THEMIDA__
VM_START
#endif
std::string fileNamelower = rfileName;
stl_lowers(fileNamelower);
std::string extName = GetFileExt(fileNamelower);
TCSHybridCryptKeyMap::const_iterator cit = m_mapHybridCryptKey.find( stringhash().GetHash(extName));
if( cit == m_mapHybridCryptKey.end() )
{
//TODO : log no file ext info
return false;
}
DWORD dwfileNameCrc = GetCRC32(fileNamelower.c_str(), fileNamelower.size());
//make file specific algorithm & key & iv
eAlgorithm = (eHybridCipherAlgorithm)(dwfileNameCrc % Num_Of_Ciphers);
::memcpy(key.key, cit->second.uEncryptKey.key, sizeof(key) );
::memcpy(iv.iv, cit->second.uEncryptIV.iv, sizeof(iv) );
//Themida Warning
for( int i = 0; i < (sizeof(key)/sizeof(dwfileNameCrc)); ++i)
{
*((DWORD*)key.key + i) ^= dwfileNameCrc;
}
for( int i = 0; i < (sizeof(iv)/sizeof(dwfileNameCrc)); ++i)
{
*((DWORD*)iv.iv + i) ^= dwfileNameCrc;
}
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::EncryptMemory( std::string& rfileName, IN const BYTE* pSrcData, IN int iSrcLen, OUT CLZObject& zObj )
{
#ifdef __THEMIDA__
VM_START
#endif
eHybridCipherAlgorithm eAlgorithm;
TEncryptKey key;
TEncryptIV iv;
if( !GetPerFileCryptKey( rfileName, eAlgorithm, key, iv ) )
{
return false;
}
// start cipher
std::string strCipher;
//NOTE : ciphered stream size could be different from original size if you choose diffrent cipher mode & algorithm
//( i.e ECB or CBC mode )
strCipher.reserve(iSrcLen);
if( eAlgorithm == e_Cipher_Camellia )
{
// Encryptor
CIPHER_MODE<Camellia>::Encryption Encryptor;
Encryptor.SetKeyWithIV(key.keyCamellia, sizeof(key.keyCamellia), iv.ivCamellia, sizeof(iv.ivCamellia));
ArraySource(pSrcData, iSrcLen, true,
new StreamTransformationFilter(Encryptor, new CryptoPP::StringSink(strCipher)));
}
else if( eAlgorithm == e_Cipher_Twofish )
{
// Encryptor
CIPHER_MODE<Twofish>::Encryption Encryptor;
Encryptor.SetKeyWithIV(key.keyTwofish, sizeof(key.keyTwofish), iv.ivTwofish, sizeof(iv.ivTwofish));
ArraySource(pSrcData, iSrcLen, true,
new StreamTransformationFilter(Encryptor, new CryptoPP::StringSink(strCipher)));
}
else if( eAlgorithm == e_Cipher_XTEA )
{
// Encryptor
CIPHER_MODE<XTEA>::Encryption Encryptor;
Encryptor.SetKeyWithIV(key.keyXTEA, sizeof(key.keyXTEA), iv.ivXTEA, sizeof(iv.ivXTEA));
ArraySource(pSrcData, iSrcLen, true,
new StreamTransformationFilter(Encryptor, new CryptoPP::StringSink(strCipher)));
}
if (strCipher.length() != iSrcLen)
{
//TODO: size error log
return false;
}
zObj.AllocBuffer(iSrcLen);
memcpy(zObj.GetBuffer(), strCipher.c_str(), strCipher.length() );
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::DecryptMemory( std::string& rfilename, IN const BYTE* pEncryptedData, IN int iEncryptedLen, OUT CLZObject& zObj )
{
#ifdef __THEMIDA__
VM_START
#endif
eHybridCipherAlgorithm eAlgorithm;
TEncryptKey key;
TEncryptIV iv;
if( !GetPerFileCryptKey( rfilename, eAlgorithm, key, iv ) )
{
return false;
}
// start decipher
std::string strDecipher;
//NOTE : ciphered stream size could be different from original size if you choose diffrent cipher mode & algorithm
//( i.e ECB or CBC mode )
strDecipher.reserve(iEncryptedLen);
if( eAlgorithm == e_Cipher_Camellia )
{
// Decryptor
CIPHER_MODE<Camellia>::Decryption Decryptor;
Decryptor.SetKeyWithIV(key.keyCamellia, sizeof(key.keyCamellia), iv.ivCamellia, sizeof(iv.ivCamellia));
ArraySource(pEncryptedData, iEncryptedLen, true,
new StreamTransformationFilter(Decryptor, new CryptoPP::StringSink(strDecipher)));
}
else if( eAlgorithm == e_Cipher_Twofish )
{
// Decryptor
CIPHER_MODE<Twofish>::Decryption Decryptor;
Decryptor.SetKeyWithIV(key.keyTwofish, sizeof(key.keyTwofish), iv.ivTwofish, sizeof(iv.ivTwofish));
ArraySource(pEncryptedData, iEncryptedLen, true,
new StreamTransformationFilter(Decryptor, new CryptoPP::StringSink(strDecipher)));
}
else if( eAlgorithm == e_Cipher_XTEA )
{
// Decryptor
CIPHER_MODE<XTEA>::Decryption Decryptor;
Decryptor.SetKeyWithIV(key.keyXTEA, sizeof(key.keyXTEA), iv.ivXTEA, sizeof(iv.ivXTEA));
ArraySource(pEncryptedData, iEncryptedLen, true,
new StreamTransformationFilter(Decryptor, new CryptoPP::StringSink(strDecipher)));
}
if (strDecipher.length() != iEncryptedLen)
{
//TODO: size error log
return false;
}
zObj.AllocBuffer(iEncryptedLen);
memcpy(zObj.GetBuffer(), strDecipher.c_str(), strDecipher.length() );
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
void EterPackPolicy_CSHybridCrypt::WriteCryptKeyToFile( CFileBase& rFile )
{
// ext cnt 4byte
// for ext hash ( crc32 )
// key-16byte
// iv-16byte
DWORD dwCryptKeySize = m_mapHybridCryptKey.size();
rFile.Write( &dwCryptKeySize, sizeof(DWORD) );
TCSHybridCryptKeyMap::const_iterator cit;
for( cit = m_mapHybridCryptKey.begin(); cit != m_mapHybridCryptKey.end(); ++cit )
{
DWORD extNamehash = cit->first;
const TCSHybridCryptKey& CryptKey = cit->second;
rFile.Write( &extNamehash, sizeof(DWORD) );
rFile.Write( CryptKey.uEncryptKey.key, sizeof(TEncryptKey) );
rFile.Write( CryptKey.uEncryptIV.iv, sizeof(TEncryptIV) );
}
}
int EterPackPolicy_CSHybridCrypt::ReadCryptKeyInfoFromStream( IN const BYTE* pStream )
{
int iStreamOffset = 0;
DWORD dwCryptoInfoSize;
memcpy(&dwCryptoInfoSize, pStream, sizeof(DWORD) );
iStreamOffset += sizeof(DWORD);
DWORD dwExtHash;
m_mapHybridCryptKey.clear();
for( int i = 0; i < dwCryptoInfoSize; ++i )
{
memcpy(&dwExtHash, pStream + iStreamOffset, sizeof(DWORD) );
iStreamOffset += sizeof(DWORD);
TCSHybridCryptKey info;
{
memcpy(info.uEncryptKey.key, pStream + iStreamOffset, sizeof(TEncryptKey) );
iStreamOffset += sizeof(TEncryptKey);
memcpy(info.uEncryptIV.iv, pStream + iStreamOffset, sizeof(TEncryptIV) );
iStreamOffset += sizeof(TEncryptIV);
}
m_mapHybridCryptKey[dwExtHash] = info;
}
return iStreamOffset;
}
bool EterPackPolicy_CSHybridCrypt::GenerateSupplementaryDataBlock(std::string& rfilename, const std::string& strMapName, IN const BYTE* pSrcData, IN int iSrcLen, OUT LPBYTE& pDestData, OUT int& iDestLen )
{
#ifdef __THEMIDA__
VM_START
#endif
std::string fileNamelower = rfilename;
stl_lowers( fileNamelower );
DWORD dwFileNameHash = stringhash().GetHash(fileNamelower);
TSupplementaryDataBlockMap::const_iterator cit = m_mapSDBMap.find( dwFileNameHash );
if( cit != m_mapSDBMap.end() )
{
//TODO : log already registered
return false;
}
//TODO : Find Better Method for deciding SDB Postion & Size
//prevent stream copy duplication
TSupplementaryDataBlockInfo info;
m_mapSDBMap[dwFileNameHash] = info;
std::string& strRelatedMapName = m_mapSDBMap[dwFileNameHash].strRelatedMapName;
std::vector<BYTE>& sdbVector = m_mapSDBMap[dwFileNameHash].vecStream;
//fill the data!!
{
strRelatedMapName = strMapName;
int iSDBSize = random_range( 64, 128 );
if( iSrcLen < iSDBSize )
{
iSDBSize = iSrcLen - 1;
if( iSDBSize <= 0 )
{
//TODO : is there 1byte file exist???
return false;
}
}
sdbVector.resize( iSDBSize );
iDestLen = iSrcLen - iSDBSize;
pDestData = (LPBYTE)pSrcData;
memcpy( &sdbVector[0], pDestData + iDestLen, iSDBSize );
}
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::GetSupplementaryDataBlock( std::string& rfilename, OUT LPBYTE& pSDB, OUT int& iSDBSize )
{
#ifdef __THEMIDA__
VM_START
#endif
std::string fileNamelower = rfilename;
stl_lowers( fileNamelower );
DWORD dwFileNameHash = stringhash().GetHash(fileNamelower);
TSupplementaryDataBlockMap::const_iterator cit = m_mapSDBMap.find( dwFileNameHash );
if( cit == m_mapSDBMap.end() )
{
//TODO : log already registered
return false;
}
const std::vector<BYTE>& vecSDB = cit->second.vecStream;
iSDBSize = vecSDB.size();
if(iSDBSize <= 0)
{
pSDB = NULL;
return false;
}
pSDB = (BYTE*)&vecSDB[0];
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::IsContainingSDBFile() const
{
return m_mapSDBMap.size() > 0 ? true : false;
}
void EterPackPolicy_CSHybridCrypt::WriteSupplementaryDataBlockToFile( CFileBase& rFile )
{
//about SDB data
// sdb file cnt( 4byte )
// for sdb file cnt
// filename hash ( stl.h stringhash )
// related map name size(4), relate map name
// sdb block size( 1byte )
// sdb blocks
DWORD dwSDBMapSize = m_mapSDBMap.size();
rFile.Write( &dwSDBMapSize, sizeof(DWORD) );
TSupplementaryDataBlockMap::const_iterator cit;
for( cit = m_mapSDBMap.begin(); cit != m_mapSDBMap.end(); ++cit )
{
DWORD dwFileNamehash = cit->first;
rFile.Write( &dwFileNamehash, sizeof(DWORD) );
const std::string strRelatedMapName = cit->second.strRelatedMapName;
DWORD dwMapNameSize = strRelatedMapName.size();
rFile.Write( &dwMapNameSize, sizeof(DWORD) );
rFile.Write( strRelatedMapName.c_str(), dwMapNameSize );
const std::vector<BYTE>& sdbVector = cit->second.vecStream;
BYTE bSDBSize = (BYTE)(sdbVector.size());
rFile.Write( &bSDBSize, sizeof(bSDBSize) );
if( bSDBSize > 0 )
rFile.Write( &sdbVector[0], bSDBSize );
}
}
int EterPackPolicy_CSHybridCrypt::ReadSupplementatyDataBlockFromStream( IN const BYTE* pStream )
{
#ifdef __THEMIDA__
VM_START
#endif
//DWORD dwFileIdentifier;
//std::vector<BYTE> vecSDBStream;
DWORD dwFileNameHash;
BYTE bSDBSize;
int iStreamOffset = 0;
memcpy(&dwFileNameHash, pStream + iStreamOffset, sizeof(DWORD) );
iStreamOffset += sizeof(DWORD);
memcpy(&bSDBSize, pStream + iStreamOffset, sizeof(BYTE) );
iStreamOffset += sizeof(BYTE);
// NOTE : related map name isn`t required in client. so we don`t recv it from stream to reduce packet size.
TSupplementaryDataBlockInfo info;
{
info.vecStream.resize( bSDBSize );
memcpy(&info.vecStream[0], pStream + iStreamOffset, bSDBSize );
iStreamOffset += bSDBSize;
m_mapSDBMap[dwFileNameHash] = info;
}
#ifdef __THEMIDA__
VM_END
#endif
return iStreamOffset;
}
@@ -0,0 +1,94 @@
#ifndef __INC_ETERPACKLIB_ETERPACKPOLICY_CSHYBRIDCRYPT_H__
#define __INC_ETERPACKLIB_ETERPACKPOLICY_CSHYBRIDCRYPT_H__
#include <unordered_map>
#include <cryptopp/cryptlib.h>
#include <cryptopp/camellia.h>
#include <cryptopp/twofish.h>
#include <cryptopp/tea.h>
enum eHybridCipherAlgorithm
{
e_Cipher_Camellia,
e_Cipher_Twofish,
e_Cipher_XTEA,
Num_Of_Ciphers
};
class CFileBase;
class CLZObject;
//THEMIDA
class EterPackPolicy_CSHybridCrypt
{
public:
~EterPackPolicy_CSHybridCrypt();
bool GenerateCryptKey( std::string& rfileName );
bool EncryptMemory( std::string& rfilename, IN const BYTE* pSrcData, IN int iSrcLen, OUT CLZObject& zObj );
bool DecryptMemory( std::string& rfilename, IN const BYTE* pSrcData, IN int iSrcLen, OUT CLZObject& zObj );
bool IsContainingCryptKey() const;
//Supplementary Data Block (SDB)
bool GenerateSupplementaryDataBlock(std::string& rfilename, const std::string& strMapName, IN const BYTE* pSrcData, IN int iSrcLen, OUT LPBYTE& pDestData, OUT int& iDestLen );
bool GetSupplementaryDataBlock( std::string& rfilename, OUT LPBYTE& pSDB, OUT int& iSDBSize );
bool IsContainingSDBFile() const;
// Read/Write IO
void WriteCryptKeyToFile( CFileBase& rFile );
int ReadCryptKeyInfoFromStream( IN const BYTE* pStream );
void WriteSupplementaryDataBlockToFile( CFileBase& rFile );
int ReadSupplementatyDataBlockFromStream( IN const BYTE* pStream );
protected:
typedef union UEncryptKey
{
BYTE key[16];
BYTE keyCamellia[ CryptoPP::Camellia::DEFAULT_KEYLENGTH];
BYTE keyTwofish [ CryptoPP::Twofish::DEFAULT_KEYLENGTH];
BYTE keyXTEA [ CryptoPP::XTEA::DEFAULT_KEYLENGTH];
} TEncryptKey;
typedef union UEncryptIV
{
BYTE iv[16];
BYTE ivCamellia [ CryptoPP::Camellia::BLOCKSIZE];
BYTE ivTwofish [ CryptoPP::Twofish::BLOCKSIZE];
BYTE ivXTEA [ CryptoPP::XTEA::BLOCKSIZE];
} TEncryptIV;
typedef struct SCSHybridCryptKey
{
TEncryptKey uEncryptKey;
TEncryptIV uEncryptIV;
} TCSHybridCryptKey;
typedef std::unordered_map<DWORD, TCSHybridCryptKey> TCSHybridCryptKeyMap;
TCSHybridCryptKeyMap m_mapHybridCryptKey;
typedef struct SSupplementaryDataBlockInfo
{
std::string strRelatedMapName;
std::vector<BYTE> vecStream;
} TSupplementaryDataBlockInfo;
typedef std::unordered_map<DWORD, TSupplementaryDataBlockInfo> TSupplementaryDataBlockMap; //key filename hash
TSupplementaryDataBlockMap m_mapSDBMap;
private:
bool GetPerFileCryptKey( std::string& rfileName, eHybridCipherAlgorithm& eAlgorithm, TEncryptKey& key, TEncryptIV& iv );
};
#endif // __INC_ETERPACKLIB_ETERPACKPOLICY_CSHYBRIDCRYPT_H__
+34
View File
@@ -0,0 +1,34 @@
#ifndef __INC_ETERPACK_INLINE_H__
#define __INC_ETERPACK_INLINE_H__
inline void inlinePathCreate(const char* path)
{
char dir[64];
const char* p, *k;
p = path + 3;
while (NULL != (k = strchr(p, '/')))
{
memset(dir, 0, sizeof(dir));
strncpy(dir, path, k - path);
CreateDirectory(dir, NULL);
p = k + 1;
}
}
inline void inlineConvertPackFilename(char* name)
{
char * p = name;
while (*p)
{
if (*p == '\\')
*p = '/';
else
*p = (int) tolower(*p);
p++;
}
}
#endif
+2
View File
@@ -0,0 +1,2 @@
#include "StdAfx.h"
+23
View File
@@ -8,6 +8,9 @@
#include <new>
#include <vector>
#include <sys/stat.h>
#include <unistd.h>
static_assert(sizeof(std::recursive_mutex) <= sizeof(CRITICAL_SECTION::storage), "CRITICAL_SECTION storage");
static_assert(alignof(std::recursive_mutex) <= 16, "CRITICAL_SECTION alignment");
@@ -21,6 +24,26 @@ void DeleteCriticalSection(LPCRITICAL_SECTION cs) { cs_mutex(cs).~recursive_mute
void EnterCriticalSection(LPCRITICAL_SECTION cs) { cs_mutex(cs).lock(); }
void LeaveCriticalSection(LPCRITICAL_SECTION cs) { cs_mutex(cs).unlock(); }
BOOL CreateDirectory(LPCSTR path, void*)
{
return mkdir(path, 0777) == 0 ? TRUE : FALSE;
}
BOOL DeleteFile(LPCSTR path)
{
return unlink(path) == 0 ? TRUE : FALSE;
}
int _access(const char* path, int mode)
{
int m = F_OK;
if (mode & 2)
m |= W_OK;
if (mode & 4)
m |= R_OK;
return access(path, m);
}
char* _strlwr(char* s)
{
for (char* p = s; *p; ++p)
+14
View File
@@ -12,6 +12,14 @@
#if !defined(_WIN32)
#include <strings.h>
#include "Win32MinMax.h"
// mmsystem.h
#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
#endif
#define ZeroMemory(dst, len) std::memset((dst), 0, (len))
#define FillMemory(dst, len, fill) std::memset((dst), (fill), (len))
#define CopyMemory(dst, src, len) std::memcpy((dst), (src), (len))
@@ -41,6 +49,12 @@ int _snprintf(char* buf, size_t count, const char* fmt, ...)
#endif
;
// kernel32 file-system calls (ANSI). CreateDirectory ignores the security attributes; _access takes
// the MSVC modes 0 (exists), 2 (write), 4 (read), 6 (read/write).
BOOL CreateDirectory(LPCSTR path, void* security_attributes);
BOOL DeleteFile(LPCSTR path);
int _access(const char* path, int mode);
// winmm `timeGetTime`: milliseconds since system start, wrapping at 2^32.
DWORD timeGetTime();
DWORD GetTickCount();
+13
View File
@@ -0,0 +1,13 @@
#pragma once
// windef.h `min`/`max` macros (40250 builds without NOMINMAX), as functions so <algorithm> and
// numeric_limits<T>::max() keep working: same usual-arithmetic-conversion result as the macro, and a
// same-typed call under `using namespace std` still resolves to the more specialized std::min/max.
// Included by Win32Crt.h off Windows, and force-included on MinGW, whose <windows.h> leaves them out in C++.
#include <type_traits>
#if !defined(min) && !defined(max)
template <class A, class B>
constexpr std::common_type_t<A, B> min(A a, B b) { return (a < b) ? a : b; }
template <class A, class B>
constexpr std::common_type_t<A, B> max(A a, B b) { return (a > b) ? a : b; }
#endif
@@ -0,0 +1,3 @@
#pragma once
// Shim for 40250's <cryptopp/cryptoppLibLink.h>, which only holds MSVC `#pragma comment(lib, ...)` auto-link
// lines; the vendored Crypto++ (mt3p::cryptopp) is linked by CMake.
@@ -0,0 +1,3 @@
#pragma once
// Shim for 40250's <lzo/lzoLibLink.h>, which only holds MSVC `#pragma comment(lib, ...)` auto-link lines;
// the vendored minilzo (mt3p::minilzo) is linked by CMake.
@@ -0,0 +1,3 @@
#pragma once
// Shim for <io.h> on non-Windows targets; what 40250 logic uses from it (_access) is in <windows.h>.
#include "windows.h"
+176
View File
@@ -0,0 +1,176 @@
// port/EterPack against the real 40250 Client/pack (batch 2D). Registers every pack in pack/Index order the way
// UserInterface.cpp PackInitialize does, then checks the numbers batch 2R measured with tools/epk_scan
// (audit/packs/2R-scan.json): 103 packs + root / 54891 entries / 52609 paths, first-registered pack wins, and exactly
// the 4 short-layout SECURITY files 40250 rejects fail to load.
//
// port_eterpack_test <40250 Client dir>
//
// Exit 77 (ctest SKIP) when the client is missing, unless MT_ASSETS_STRICT=1, which turns that into a failure.
#include "EterPack/StdAfx.h"
#include "EterPack/EterPackManager.h"
#include "EterBase/CRC32.h"
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <dirent.h>
#include <strings.h>
#include <unistd.h>
static int g_failures = 0;
#define CHECK(cond) \
do { \
if (!(cond)) { \
std::fprintf(stderr, "%s:%d: CHECK(%s)\n", __FILE__, __LINE__, #cond); \
++g_failures; \
} \
} while (0)
// Index names are spelled as on the NTFS original ("Etc" for ETC.eix); find the on-disk spelling.
static std::string on_disk_name(const std::string& dir, const std::string& name)
{
std::string found = name;
if (DIR* d = opendir(dir.c_str()))
{
const std::string want = name + ".eix";
while (dirent* e = readdir(d))
if (strcasecmp(e->d_name, want.c_str()) == 0)
found = std::string(e->d_name, want.size() - 4);
closedir(d);
}
return found;
}
static std::vector<std::string> read_lines(const std::string& path)
{
std::vector<std::string> lines;
std::ifstream in(path, std::ios::binary);
std::string line;
while (std::getline(in, line))
{
while (!line.empty() && (line.back() == '\r' || line.back() == ' ' || line.back() == '\t'))
line.pop_back();
lines.push_back(line);
}
return lines;
}
int main(int argc, char** argv)
{
const char* strict = std::getenv("MT_ASSETS_STRICT");
const bool is_strict = strict && std::string(strict) == "1";
const std::string client = argc > 1 ? argv[1] : "";
if (client.empty() || chdir(client.c_str()) != 0 || access("pack/Index", 0) != 0)
{
std::fprintf(stderr, "port_eterpack_test: no 40250 Client/pack at '%s'\n", client.c_str());
return is_strict ? 1 : 77;
}
// PackInitialize("pack"): line 0 is PACK, then (folder, name) pairs; every name also gets a _texcache pack.
const std::vector<std::string> lines = read_lines("pack/Index");
CHECK(!lines.empty() && lines[0] == "PACK");
CEterPackManager manager; // CSingleton: the instance registers itself
CEterPackManager& mgr = CEterPackManager::Instance();
mgr.SetCacheMode();
mgr.SetSearchMode(true);
std::vector<std::string> registered;
for (size_t i = 1; i + 1 < lines.size(); i += 2)
{
const std::string& folder = lines[i];
for (const std::string& name : {lines[i + 1], lines[i + 1] + "_texcache"})
{
const std::string db = "pack/" + on_disk_name("pack", name);
if (mgr.RegisterPack(db.c_str(), folder.c_str()))
registered.push_back(db);
}
}
mgr.RegisterRootPack("pack/root");
// 124 Index pairs; 119 registrations find a pack (some names are listed twice), 103 distinct packs, + root.
CHECK(registered.size() == 119);
CHECK(std::set<std::string>(registered.begin(), registered.end()).size() == 103);
// The whole dict (one entry per pack index record, overrides included).
struct Access : CEterPackManager
{
static const CEterFileDict::TDict& dict(CEterPackManager& m) { return static_cast<Access&>(m).m_FileDict.GetDict(); }
static CEterFileDict::Item* item(CEterPackManager& m, DWORD h, const char* n) { return static_cast<Access&>(m).m_FileDict.GetItem(h, n); }
};
const CEterFileDict::TDict& dict = Access::dict(mgr);
CHECK(dict.size() == 54891);
std::map<std::string, int> order;
for (size_t i = 0; i < registered.size(); ++i)
order[registered[i]] = static_cast<int>(i);
// First registration per path, and CRC32 of the name equal to the stored filename_crc.
std::map<std::string, std::string> first;
for (const auto& kv : dict)
{
const std::string name = kv.second.pkInfo->filename;
const std::string pack = kv.second.pkPack->GetDBName();
CHECK(GetCRC32(name.c_str(), name.size()) == kv.first);
auto it = first.find(name);
if (it == first.end() || order[pack] < order[it->second])
first[name] = pack;
}
CHECK(first.size() == 52609);
size_t overridden = 0, wrong_winner = 0;
std::map<std::string, int> per_name;
for (const auto& kv : dict)
++per_name[kv.second.pkInfo->filename];
for (const auto& [name, count] : per_name)
{
if (count < 2)
continue;
overridden += count - 1;
CEterFileDict::Item* item = Access::item(mgr, GetCRC32(name.c_str(), name.size()), name.c_str());
if (!item || first[name] != item->pkPack->GetDBName())
{
if (wrong_winner < 5)
std::fprintf(stderr, "winner %s: %s, expected %s\n", name.c_str(),
item ? item->pkPack->GetDBName() : "(none)", first[name].c_str());
++wrong_winner;
}
}
CHECK(overridden == 2282);
CHECK(wrong_winner == 0);
// Every path loads through CEterPackManager::Get, except the 4 short-layout SECURITY files.
const std::set<std::string> rejected_expected = {
"d:/ymir work/npc/mineral2/wait.msa",
"d:/ymir work/effect/background/metinstone_loop_redblack.mse",
"d:/ymir work/monster2/spider_spawn/wait.msa",
"d:/ymir work/monster2/spider_spawn/line1.mse",
};
std::set<std::string> rejected;
unsigned long long bytes = 0;
for (const auto& [name, pack] : first)
{
CMappedFile file;
LPCVOID data = nullptr;
if (mgr.Get(file, name.c_str(), &data))
bytes += file.Size();
else
rejected.insert(name);
}
for (const std::string& name : rejected)
if (!rejected_expected.count(name))
std::fprintf(stderr, "unexpected load failure: %s\n", name.c_str());
CHECK(rejected == rejected_expected);
std::printf("packs %zu, entries %zu, paths %zu, overrides %zu, loaded %zu (%llu bytes), rejected %zu\n",
registered.size(), dict.size(), first.size(), overridden, first.size() - rejected.size(), bytes,
rejected.size());
if (g_failures)
std::fprintf(stderr, "%d failure(s)\n", g_failures);
return g_failures ? 1 : 0;
}
+2 -2
View File
@@ -33,7 +33,7 @@ for p in "${PLATFORMS[@]}"; do
BUILD_EXTRA=()
case "$p" in
macos)
BUILD_EXTRA=(--target port_common_test)
BUILD_EXTRA=(--target port_common_test --target port_eterpack_test)
run macos "$OUT/macos" "${PORT_ONLY[@]}" -DBUILD_TESTING=ON
last=$((${#RESULTS[@]} - 1))
if [[ "${RESULTS[$last]}" == *PASS ]] && ! ctest --test-dir "$OUT/macos" -R '^port\.' --output-on-failure >>"$OUT/macos.log" 2>&1; then
@@ -71,7 +71,7 @@ for p in "${PLATFORMS[@]}"; do
;;
linux)
if [ "$(uname -s)" = Linux ]; then
BUILD_EXTRA=(--target port_common_test)
BUILD_EXTRA=(--target port_common_test --target port_eterpack_test)
run linux "$OUT/linux" "${PORT_ONLY[@]}" -DBUILD_TESTING=ON
elif [ -n "${MT_LINUX_TOOLCHAIN:-}" ]; then
run linux "$OUT/linux" "${PORT_ONLY[@]}" -DBUILD_TESTING=OFF -DCMAKE_TOOLCHAIN_FILE="$MT_LINUX_TOOLCHAIN"