#pragma once #include #include #include #include #include #include #include #include #include #include #ifdef _CONSOLE #include #include #endif #include "nstring.h" #include "localeex.h" #include "themeinfo.h" // #define UNALIGN_MEMORY #ifdef UNALIGN_MEMORY #pragma pack(push, 1) #endif #ifdef min #undef min #endif #ifdef max #undef max #endif struct destruct { std::function endtask = nullptr; destruct (std::function init): endtask (init) {} ~destruct () { if (endtask) endtask (); } }; template struct LargeIntBase { BASE_INT val; LargeIntBase () { val.QuadPart = 0; } LargeIntBase (T v) { val.QuadPart = v; } LargeIntBase (const LargeIntBase &other) { val.QuadPart = other.val.QuadPart; } LargeIntBase (const BASE_INT &other) { val = other; } operator BASE_INT () const { return val; } operator T () const { return val.QuadPart; } explicit operator bool () const { return val.QuadPart != 0; } T *ptr_num () { return &val.QuadPart; } BASE_INT *ptr_union () { return &val; } size_t sizeof_num () const { return sizeof (val.QuadPart); } size_t sizeof_union () const { return sizeof (val); } T num () const { return val.QuadPart; } BASE_INT win_union () const { return val; } LargeIntBase operator + (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart + rhs.val.QuadPart); } LargeIntBase operator - (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart - rhs.val.QuadPart); } LargeIntBase operator * (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart * rhs.val.QuadPart); } LargeIntBase operator / (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart / rhs.val.QuadPart); } LargeIntBase operator % (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart % rhs.val.QuadPart); } LargeIntBase &operator += (const LargeIntBase &rhs) { val.QuadPart += rhs.val.QuadPart; return *this; } LargeIntBase &operator -= (const LargeIntBase &rhs) { val.QuadPart -= rhs.val.QuadPart; return *this; } LargeIntBase &operator *= (const LargeIntBase &rhs) { val.QuadPart *= rhs.val.QuadPart; return *this; } LargeIntBase &operator /= (const LargeIntBase &rhs) { val.QuadPart /= rhs.val.QuadPart; return *this; } LargeIntBase &operator %= (const LargeIntBase &rhs) { val.QuadPart %= rhs.val.QuadPart; return *this; } LargeIntBase operator & (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart & rhs.val.QuadPart); } LargeIntBase operator | (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart | rhs.val.QuadPart); } LargeIntBase operator ^ (const LargeIntBase &rhs) const { return LargeIntBase (val.QuadPart ^ rhs.val.QuadPart); } LargeIntBase operator << (int n) const { return LargeIntBase (val.QuadPart << n); } LargeIntBase operator >> (int n) const { return LargeIntBase (val.QuadPart >> n); } LargeIntBase &operator &= (const LargeIntBase &rhs) { val.QuadPart &= rhs.val.QuadPart; return *this; } LargeIntBase &operator |= (const LargeIntBase &rhs) { val.QuadPart |= rhs.val.QuadPart; return *this; } LargeIntBase &operator ^= (const LargeIntBase &rhs) { val.QuadPart ^= rhs.val.QuadPart; return *this; } LargeIntBase &operator <<= (int n) { val.QuadPart <<= n; return *this; } LargeIntBase &operator >>= (int n) { val.QuadPart >>= n; return *this; } LargeIntBase &operator ++ () { ++val.QuadPart; return *this; } LargeIntBase operator ++ (int) { LargeIntBase tmp (*this); ++val.QuadPart; return tmp; } LargeIntBase &operator -- () { --val.QuadPart; return *this; } LargeIntBase operator -- (int) { LargeIntBase tmp (*this); --val.QuadPart; return tmp; } bool operator < (const LargeIntBase &rhs) const { return val.QuadPart < rhs.val.QuadPart; } bool operator > (const LargeIntBase &rhs) const { return val.QuadPart > rhs.val.QuadPart; } bool operator <= (const LargeIntBase &rhs) const { return val.QuadPart <= rhs.val.QuadPart; } bool operator >= (const LargeIntBase &rhs) const { return val.QuadPart >= rhs.val.QuadPart; } bool operator == (const LargeIntBase &rhs) const { return val.QuadPart == rhs.val.QuadPart; } bool operator != (const LargeIntBase &rhs) const { return val.QuadPart != rhs.val.QuadPart; } bool operator ! () const { return !val.QuadPart; } LargeIntBase operator ~ () const { return LargeIntBase (~val.QuadPart); } }; typedef LargeIntBase LargeInt, lint; typedef LargeIntBase ULargeInt, ulint; template , typename al = std::allocator > class basic_priid: public std::basic_nstring { using base = std::basic_nstring ; public: using typename base::size_type; using typename base::value_type; using base::base; basic_priid (const ct *buf, size_t sz = 16): base (buf, sz) {} template basic_priid (const ct (&arr) [N]) : base (arr, N) {} }; typedef basic_priid pri_sectid; std::string ReadStringEndwithNullA (IStream *pStream) { if (!pStream) return ""; std::string result; char ch = 0; ULONG cbRead = 0; while (true) { HRESULT hr = pStream->Read (&ch, 1, &cbRead); if (FAILED (hr) || cbRead == 0) break; if (ch == '\0') break; result.push_back (ch); } return result; } std::wstring ReadStringEndwithNullW (IStream *pStream) { if (!pStream) return L""; std::wstring result; WCHAR ch = 0; ULONG cbRead = 0; while (true) { HRESULT hr = pStream->Read (&ch, sizeof (WCHAR), &cbRead); if (FAILED (hr) || cbRead < sizeof (WCHAR)) break; if (ch == L'\0') break; result.push_back (ch); } return result; } std::string ReadStringEndwithNull (IStream *ifile, std::string &ret) { return ret = ReadStringEndwithNullA (ifile); } std::wstring ReadStringEndwithNull (IStream *ifile, std::wstring &ret) { return ret = ReadStringEndwithNullW (ifile); } std::string ReadStringA (IStream *pStream, size_t length) { if (!pStream || length <= 0) return ""; std::string result; result.resize (length); ULONG cbRead = 0; HRESULT hr = pStream->Read (&result [0], length, &cbRead); if (FAILED (hr) || cbRead == 0) { result.clear (); return result; } if (cbRead < (ULONG)length) result.resize (cbRead); return result; } std::wstring ReadStringW (IStream *pStream, size_t length) { if (!pStream || length <= 0) return L""; std::wstring result; result.resize (length); ULONG cbRead = 0; HRESULT hr = pStream->Read (&result [0], length * sizeof (WCHAR), &cbRead); if (FAILED (hr) || cbRead == 0) { result.clear (); return result; } if (cbRead < (ULONG)(length * sizeof (WCHAR))) result.resize (cbRead / sizeof (WCHAR)); return result; } enum class seekpos: DWORD { start = STREAM_SEEK_SET, current = STREAM_SEEK_CUR, end = STREAM_SEEK_END }; class bytesstream { std::vector bytes; size_t pos = 0; public: using seekpos = ::seekpos; // 文件长度/绝对偏移位置。(都是 0 起始) using fsize_t = uint64_t; // 文件指针相对移动长度,正数向右,负数向左 using fmove_t = int64_t; bool read (void *p, size_t size, size_t *ret = nullptr) { if (!p || size == 0) return false; size_t available = 0; if (pos < bytes.size ()) available = bytes.size () - pos; size_t readSize = (available >= size) ? size : available; if (readSize > 0) memcpy_s (p, size, bytes.data () + pos, readSize); else memset (p, 0, size); pos += size; if (ret) *ret = readSize; return true; } template ::value>::type> size_t read (T &var) { size_t ret = 0; read (&var, sizeof (var), &ret); return ret; } template ::value>::type> friend bytesstream &operator >> (bytesstream &s, T &variable) { s.read (&variable, sizeof (variable)); return s; } friend bytesstream &operator >> (bytesstream &in, std::string &ret) { in.read_string_endwith_null (ret); return in; } friend bytesstream &operator >> (bytesstream &in, std::wstring &ret) { in.read_string_endwith_null (ret); return in; } bytesstream (const std::vector &vec): bytes (vec) {} bytesstream (const BYTE *ptr, size_t len = 0): bytes (len, *ptr) {} bytesstream () = default; auto &data () { return bytes; } void set (const std::vector &bver) { bytes = bver; } auto position () { if (pos > bytes.size ()) pos = bytes.size (); return pos; } auto position (size_t v) { pos = v; if (pos > bytes.size ()) pos = bytes.size (); return pos; } auto length () const { return bytes.size (); } auto size () const { return length (); } std::string read_string_endwith_null_a (); std::wstring read_string_endwith_null_w (); size_t read_string_endwith_null (std::string &ret); size_t read_string_endwith_null (std::wstring &ret); std::string read_string_a (size_t length); std::wstring read_string_w (size_t length); size_t read_string (size_t length, std::wstring &ret); size_t read_string (size_t length, std::string &ret); HRESULT seek (fmove_t movelen = 0, seekpos origin = seekpos::current, fsize_t *newpos = nullptr) { if (bytes.empty ()) return E_INVALIDARG; int64_t newPosition = 0; switch (origin) { case seekpos::start: newPosition = movelen; break; case seekpos::current: newPosition = static_cast (pos) + movelen; break; case seekpos::end: newPosition = static_cast (bytes.size ()) + movelen; break; default: return E_INVALIDARG; } if (newPosition < 0) newPosition = 0; pos = static_cast (newPosition); if (newpos) *newpos = static_cast (pos); return S_OK; } auto &buffer () const { return bytes; } auto &buffer () { return bytes; } size_t remain () const { if (pos > size ()) return 0; return size () - pos; } void clear () { bytes.clear (); pos = 0; } void resize (size_t len) { bytes.resize (len); } const BYTE *ptr () const { return bytes.data (); } BYTE *ptr () { return bytes.data (); } template ::value>::type> T read_bytes (size_t byteslen = sizeof (T), size_t *returnread = nullptr) { std::vector bytesbuf (byteslen); size_t rl = 0; read (bytesbuf.data (), byteslen, &rl); if (returnread) *returnread = rl; bytesbuf.resize (!rl ? sizeof (T) : rl); return *(T *)bytesbuf.data (); } }; std::string ReadStringEndwithNullA (bytesstream &stream) { std::string result; char ch = 0; size_t cbRead = 0; while (true) { if (!stream.read (&ch, 1, &cbRead) || cbRead == 0) break; if (ch == '\0') break; result.push_back (ch); } return result; } std::wstring ReadStringEndwithNullW (bytesstream &stream) { std::wstring result; WCHAR ch = 0; size_t cbRead = 0; while (true) { if (!stream.read (&ch, sizeof (WCHAR), &cbRead) || cbRead < sizeof (WCHAR)) break; if (ch == L'\0') break; result.push_back (ch); } return result; } std::string ReadStringEndwithNull (bytesstream &stream, std::string &ret) { return ret = ReadStringEndwithNullA (stream); } std::wstring ReadStringEndwithNull (bytesstream &stream, std::wstring &ret) { return ret = ReadStringEndwithNullW (stream); } std::string ReadStringA (bytesstream &stream, size_t length) { if (length == 0) return ""; std::string result (length, '\0'); size_t cbRead = 0; if (!stream.read (&result [0], length, &cbRead) || cbRead == 0) { result.clear (); return result; } if (cbRead < length) result.resize (cbRead); return result; } std::wstring ReadStringW (bytesstream &stream, size_t length) { if (length == 0) return L""; std::wstring result (length, L'\0'); size_t cbRead = 0; if (!stream.read (&result [0], length * sizeof (WCHAR), &cbRead) || cbRead == 0) { result.clear (); return result; } if (cbRead < length * sizeof (WCHAR)) result.resize (cbRead / sizeof (WCHAR)); return result; } std::string bytesstream::read_string_endwith_null_a () { return ReadStringEndwithNullA (*this); } std::wstring bytesstream::read_string_endwith_null_w () { return ReadStringEndwithNullW (*this); } size_t bytesstream::read_string_endwith_null (std::string &ret) { return (ret = read_string_endwith_null_a ()).length (); } size_t bytesstream::read_string_endwith_null (std::wstring &ret) { return (ret = read_string_endwith_null_w ()).length (); } std::string bytesstream::read_string_a (size_t length) { return ReadStringA (*this, length); } std::wstring bytesstream::read_string_w (size_t length) { return ReadStringW (*this, length); } size_t bytesstream::read_string (size_t length, std::wstring &ret) { return (ret = read_string_w (length)).length (); } size_t bytesstream::read_string (size_t length, std::string &ret) { return (ret = read_string_a (length)).length (); } // 注:销毁时不会释放指针。仅是为一种操作类 class istreamstream { private: // COM 接口:流指针 IStream *comStream = nullptr; // 缓存,最长 210 字节 bytesstream bsCache; // 将缓存视为窗口,这里记录缓存相对于文件的位置。 uint64_t ulWndOffset = 0; static const size_t MAX_CACHE_SIZE = 210; public: // 文件长度/绝对偏移位置。(都是 0 起始) using fsize_t = uint64_t; // 文件指针相对移动长度,正数向右,负数向左 using fmove_t = int64_t; using seekpos = ::seekpos; istreamstream (IStream *iptr): comStream (iptr) { // bsCache.buffer ().reserve (MAX_CACHE_SIZE); } auto &buffer () const { return bsCache; } auto &buffer () { return bsCache; } void set_stream (IStream *iptr) { comStream = iptr; } IStream *get_stream () { return comStream; } // read 处理机制 // 构建缓存:会读取最长 210 字节的缓存 // 读取缓存:在缓存中读取字节,流指针与字节指针一起移动。字节指针经过计算一定与流指针相等。 HRESULT read (void *buf, size_t size, size_t *readbytes = nullptr) { if (!comStream || !buf) return E_INVALIDARG; if (position () < ulWndOffset || position () >= ulWndOffset + bsCache.size () || position () != ulWndOffset + position ()) bsCache.buffer ().clear (); if (size > MAX_CACHE_SIZE) { bsCache.clear (); ULONG cbread = 0; HRESULT hr = comStream->Read (buf, size, &cbread); if (readbytes) *readbytes = cbread; return hr; } else if (bsCache.remain () < size) { bsCache.clear (); bsCache.resize (MAX_CACHE_SIZE); auto nowpos = position (); ULONG cbread = 0; HRESULT hr = comStream->Read (bsCache.ptr (), MAX_CACHE_SIZE, &cbread); comStream->Seek (lint (nowpos), STREAM_SEEK_SET, nullptr); ulWndOffset = nowpos; bsCache.resize (cbread); size_t bufread = 0; bool res = bsCache.read (buf, size, &bufread); if (readbytes) *readbytes = bufread; comStream->Seek (lint (bufread), STREAM_SEEK_CUR, nullptr); if (res) return S_OK; else return FAILED (hr) ? hr : E_FAIL; } else { size_t bufread = 0; bool res = bsCache.read (buf, size, &bufread); if (readbytes) *readbytes = bufread; comStream->Seek (lint (bufread), STREAM_SEEK_CUR, nullptr); return res ? S_OK : E_FAIL; } } // 使用场景有限,且不支持传入指针。 template ::value>::type> HRESULT read (T &variable, size_t *readbytes = nullptr) { return read (&variable, sizeof (variable), readbytes); } std::string read_string_endwith_null_a () { return ReadStringEndwithNullA (comStream); } std::wstring read_string_endwith_null_w () { return ReadStringEndwithNullW (comStream); } size_t read_string_endwith_null (std::string &ret) { return (ret = read_string_endwith_null_a ()).length (); } size_t read_string_endwith_null (std::wstring &ret) { return (ret = read_string_endwith_null_w ()).length (); } std::string read_string_a (size_t length) { return ReadStringA (comStream, length); } std::wstring read_string_w (size_t length) { return ReadStringW (comStream, length); } size_t read_string (size_t length, std::wstring &ret) { return (ret = read_string_w (length)).length (); } size_t read_string (size_t length, std::string &ret) { return (ret = read_string_a (length)).length (); } HRESULT write (const void *bytes, size_t size, size_t *writebytes = nullptr) { if (!comStream) return E_INVALIDARG; ULONG retsize = 0; HRESULT hr = comStream->Write (bytes, size, &retsize); if (writebytes) *writebytes = retsize; return hr; } // 使用场景有限,且不支持传入指针。 template ::value>::type> HRESULT write (const T &variable, size_t *writebytes = nullptr) { return write (&variable, sizeof (variable), writebytes); } HRESULT seek (fmove_t movelen = 0, seekpos origin = seekpos::current, fsize_t *newpos = nullptr) { if (!comStream) return E_INVALIDARG; ulint ret = 0; HRESULT hr = comStream->Seek (lint (movelen), (DWORD)origin, ret.ptr_union ()); if ((movelen != 0 || origin != seekpos::current) && bsCache.size ()) bsCache.clear (); if (newpos) *newpos = ret; return hr; } fsize_t position () { fsize_t pos = 0; seek (0, seekpos::current, &pos); return pos; } HRESULT stat (STATSTG *data, DWORD statflags) { if (!comStream) return E_INVALIDARG; return comStream->Stat (data, statflags); } HRESULT size (fsize_t newsize) { if (!comStream) return E_INVALIDARG; return comStream->SetSize (ulint (newsize)); } fsize_t size () { STATSTG stg = {0}; if (SUCCEEDED (stat (&stg, STATFLAG_NONAME))) return stg.cbSize.QuadPart; else return 0; } HRESULT copy_to (istreamstream &pstm, fsize_t cb, fsize_t *pcbRead = nullptr, fsize_t *pcbWritten = nullptr) { if (!comStream) return E_INVALIDARG; ulint r = 0, w = 0; HRESULT hr = comStream->CopyTo (pstm.comStream, ulint (cb), r.ptr_union (), w.ptr_union ()); if (pcbRead) *pcbRead = r; if (pcbWritten) *pcbWritten = w; return hr; } HRESULT commit (DWORD grfCommitFlags) { if (!comStream) return E_INVALIDARG; return comStream->Commit (grfCommitFlags); } HRESULT revert () { if (!comStream) return E_INVALIDARG; return comStream->Revert (); } HRESULT lock_region (fsize_t libOffset, fsize_t cb, DWORD dwLockType) { if (!comStream) return E_INVALIDARG; return comStream->LockRegion (ulint (libOffset), ulint (cb), dwLockType); } HRESULT unlock_region (fsize_t libOffset, fsize_t cb, DWORD dwLockType) { if (!comStream) return E_INVALIDARG; return comStream->UnlockRegion (ulint (libOffset), ulint (cb), dwLockType); } HRESULT clone (istreamstream &anotherptr) { if (!comStream) return E_INVALIDARG; return comStream->Clone (&anotherptr.comStream); } template ::value>::type> friend istreamstream &operator >> (istreamstream &in, T &variable) { in.read (variable); return in; } template ::value>::type> friend istreamstream &operator << (istreamstream &out, const T &variable) { out.write (variable); return out; } friend istreamstream &operator >> (istreamstream &in, std::string &ret) { in.read_string_endwith_null (ret); return in; } friend istreamstream &operator >> (istreamstream &in, std::wstring &ret) { in.read_string_endwith_null (ret); return in; } operator IStream * () { return comStream; } IStream *operator -> () { return comStream; } template ::value>::type> // 读取一个值,并判断是否符合预期。throwerror 为真时,如果值不符合预期则通过抛出异常来判断。否则只通过返回值来判断。 bool expect (const T &expect_value, bool throwerror = true, T *retvalue = nullptr) { T value; this->read (value); if (retvalue) *retvalue = value; bool res = value == expect_value; if (!res && throwerror) throw std::exception ("Unexpected value read."); return res; } template ::value>::type> T read_bytes (size_t byteslen = sizeof (T), size_t *returnread = nullptr) { std::vector bytesbuf (byteslen); size_t rl = 0; read (bytesbuf.data (), byteslen, &rl); if (returnread) *returnread = rl; bytesbuf.resize (!rl ? sizeof (T) : rl); return *(T *)bytesbuf.data (); } }; // 0 1 // 012345678901234 #define PRI_SECT_ID_PRI_DESCRIPTOR "[mrm_pridescex]" #define PRI_SECT_ID_HIERARCHICAL_SCHEMA "[mrm_hschema] " #define PRI_SECT_ID_HIERARCHICAL_SCHEMAEX "[mrm_hschemaex]" #define PRI_SECT_ID_DECISION_INFO "[mrm_decn_info]" #define PRI_SECT_ID_RESOURCE_MAP "[mrm_res_map__]" #define PRI_SECT_ID_RESOURCE_MAP2 "[mrm_res_map2_]" #define PRI_SECT_ID_DATA_ITEM "[mrm_dataitem] " #define PRI_SECT_ID_REVERSE_MAP "[mrm_rev_map] " #define PRI_SECT_ID_REFERENCED_FILE "[def_file_list]" enum class SectionType { Unknown, // 未知类型 - 处理未识别的节区 PriDescriptor, // PRI 描述符 - 包含文件整体结构和引用信息 HierarchicalSchema, // 层次结构模式 - 定义资源命名空间和层次结构 HierarchicalSchemaEx, // 层次结构模式 - 定义资源命名空间和层次结构 DecisionInfo, // 决策信息 - 管理资源限定符和决策逻辑 ResourceMap, // 资源映射 - 将资源项映射到具体候选值 ResourceMap2, // 资源映射 - 将资源项映射到具体候选值 DataItem, // 数据项 - 存储实际的资源数据 ReverseMap, // 反向映射 - 提供从资源名到ID的映射 ReferencedFile // 引用文件 - 管理引用的外部文件 }; std::wstring EnumToStringW (SectionType value) { switch (value) { case SectionType::PriDescriptor: return L"PriDescriptor"; break; case SectionType::HierarchicalSchema: return L"HierarchicalSchema"; break; case SectionType::HierarchicalSchemaEx: return L"HierarchicalSchemaEx"; break; case SectionType::DecisionInfo: return L"DecisionInfo"; break; case SectionType::ResourceMap: return L"ResourceMap"; break; case SectionType::ResourceMap2: return L"ResourceMap2"; break; case SectionType::DataItem: return L"DataItem"; break; case SectionType::ReverseMap: return L"ReverseMap"; break; case SectionType::ReferencedFile: return L"ReferencedFile"; break; default: case SectionType::Unknown: return L"Unknown"; break; } } struct head { // 版本标识符 / version identifier // “mrm_pri0”:客户端 6.2.1(Windows 8)。 // “mrm_pri1”:客户端 6.3.0(Windows 8.1)。 // “mrm_prif”:Windows Phone 6.3.1。 // “mrm_pri2”:Universal 10.0.0(Windows 10)。 CHAR szMagic [8] = {}; WORD wPlaceholder1 = -1, // 未知,0 / unknown, zero wPlaceholder2 = 0; // 未知,1 / unknown, one DWORD dwFileSize = 0, // 文件总大小 / total file size dwToCOffset = 0, // 目录表偏移 / offset of table of contents dwSectStartOffset = 0; // 第一节的偏移 / offset of first section WORD wSectCount = 0, // 节数量 / number of sections wPlaceholder3 = 0; // 未知,0xFFFF / unknown, 0xFFFF DWORD dwPlaceholder4 = -1; // 未知,0 / unknown, zero bool valid () { CHAR m7 = szMagic [7]; destruct endt ([this, m7] () { if (m7) szMagic [7] = m7; }); szMagic [7] = '\0'; if (_stricmp (szMagic, "mrm_pri")) return false; switch (m7) { case '0': case '1': case '2': case 'f': case 'F': break; default: return false; } if (wPlaceholder1 != 0) return false; if (wPlaceholder2 != 1) return false; if (wPlaceholder3 != 0xFFFF) return false; if (dwPlaceholder4 != 0) return false; return true; } }; struct foot { DWORD dwChkCode = 0; // 0xDEFFFADE DWORD dwTotalFileSize = 0; // total file size, as in header CHAR szMagic [8] = {0}; // version identifier, as in header bool valid (const head &fh) { if (dwChkCode != 0xDEFFFADE) return false; if (dwTotalFileSize != fh.dwFileSize) return false; for (int i = 0; i < 8; i ++) { if (szMagic [i] != fh.szMagic [i] && tolower (szMagic [i]) != tolower (fh.szMagic [i])) return false; } return true; } }; struct tocentry { CHAR szIdentifier [16] = {0}; // 节标识符 / section identifier WORD wFlags = 0; // 标志 / flags WORD wSectFlags = 0; // 节标志 / section flags DWORD dwSectQualifier = 0; // 节限定符 / section qualifier DWORD dwSectOffset = 0; // 节偏移(相对于第一节偏移) / section offset (relative to offset of first section) DWORD dwSectLength = 0; // 节长度 / section length }; struct section_header { CHAR szIdentifier [16] = {0}; // 节标识符 / section identifier DWORD dwQualifier = 0; // 节限定符 / section qualifier WORD wFlags = 0; // 标志 / flags WORD wSectFlags = 0; // 节标志 / section flags DWORD dwLength = 0; // 节长度 / section length DWORD dwPlaceholder1 = -1; // 未知,0 / unknown, zero bool valid () const { return szIdentifier [0] && !dwPlaceholder1; } }; struct section_check { DWORD dwChkCode = 0; // 魔数 0xF5DEDEFA DWORD dwSectLength = 0; // 节长度(与节头中的相同) / section length, as in section header bool valid (const section_header &h) const { return dwChkCode == 0xDEF5FADE && dwSectLength == h.dwLength; } }; struct substream { IStream *&ifile; ULONGLONG offset = 0; ULONGLONG size = 0; substream (IStream *&ifile, ulint ofs = 0, ulint siz = 0): ifile (ifile), offset (ofs), size (siz) {} void set (ulint p_offset, ulint p_size) { offset = p_offset; size = p_size; } HRESULT seek () { istreamstream iss (ifile); return iss.seek (offset, istreamstream::seekpos::start); } }; struct prifile; struct section { section_header head; section_check foot; substream childst; // 请从 type 方法获取类型,而不是直接通过 sect_type 来读取(因为未初始化) SectionType sect_type = SectionType::Unknown; section (IStream *&ifile, prifile &prif): childst (ifile), pri_file (prif) {} bool valid () const { return head.valid () && foot.valid (head); } SectionType type () { if (sect_type == SectionType::Unknown) { pri_sectid pid (head.szIdentifier, 16); if (pid.equals (PRI_SECT_ID_PRI_DESCRIPTOR)) sect_type = SectionType::PriDescriptor; else if (pid.equals (PRI_SECT_ID_HIERARCHICAL_SCHEMA)) sect_type = SectionType::HierarchicalSchema; else if (pid.equals (PRI_SECT_ID_HIERARCHICAL_SCHEMAEX)) sect_type = SectionType::HierarchicalSchemaEx; else if (pid.equals (PRI_SECT_ID_DECISION_INFO)) sect_type = SectionType::DecisionInfo; else if (pid.equals (PRI_SECT_ID_RESOURCE_MAP)) sect_type = SectionType::ResourceMap; else if (pid.equals (PRI_SECT_ID_RESOURCE_MAP2)) sect_type = SectionType::ResourceMap2; else if (pid.equals (PRI_SECT_ID_DATA_ITEM)) sect_type = SectionType::DataItem; else if (pid.equals (PRI_SECT_ID_REVERSE_MAP)) sect_type = SectionType::ReverseMap; else if (pid.equals (PRI_SECT_ID_REFERENCED_FILE)) sect_type = SectionType::ReferencedFile; else sect_type = SectionType::Unknown; } return sect_type; } size_t length () const { return head.dwLength; } prifile &pri_file; #ifdef _CONSOLE friend std::wostream &operator << (std::wostream &o, section &s) { return o << L"Section " << EnumToStringW (s.type ()) << L" Length " << s.head.dwLength; } #endif }; namespace pri { struct sect_pridesp; // PriDescriptor PRI 描述符 - 包含文件整体结构和引用信息 struct sect_hierasche; // HierarchicalSchema & HierarchicalSchemaEx 层次结构模式 - 定义资源命名空间和层次结构 struct sect_decinfo; // DecisionInfo 决策信息 - 管理资源限定符和决策逻辑 struct sect_resmap; // ResourceMap & ResourceMap2 资源映射 - 将资源项映射到具体候选值 struct sect_dataitem; // DataItem 数据项 - 存储实际的资源数据 struct sect_revmap; // ReverseMap 反向映射 - 提供从资源名到ID的映射 struct sect_reffile; // ReferencedFile 引用文件 - 管理引用的外部文件 struct sect_unknown; // Unknown 未知类型 - 处理未识别的节区 // PriDescriptorFlags enum class PRI_SEC_DESP: DWORD { INVALID = 0, AUTOMERGE = 0x1, // AutoMerge DEPLOY_MERGEABLE = 0x2, // IsDeploymentMergeable DEPLOY_MERGE_RESULT = 0x4, // IsDeploymentMergeResult AUTO_MERGE_RESULT = 0x8 // IsAutomergeMergeResult }; // HierarchicalSchema & HierarchicalSchemaEx typedef struct _HSCHEMA_VERSION_INFO { WORD wMajor = 0; // 主版本号 / major version WORD wMinor = 0; // 次版本号 / minor version DWORD dwUnknown1 = -1; // 未知,0 // 校验和(checksum) // checksum: a CRC32-based checksum computed on the unique name, // the name, the section indices of the Resource Map Section // and Data Item Section, and the names of all scopes and items DWORD dwCheckSum = 0; DWORD dwScopeCount = 0; // scope 数量 DWORD dwItemCount = 0; // item 数量 bool empty () { return dwUnknown1 != 0; } } HSCHEMA_VERSION_INFO; // 资源名称(scope/item)按以下格式存储(示例字段):parent scope index、full path 长度、 // 名字首字母(大写)、名字长度、名字偏移、index property 等。 typedef struct _SCOPE_ITEM_INFO { WORD wParentScopeIndex = -1; // parent scope index WORD wFullPathLength = 0; // length of full path WCHAR wchUpperFirst = L'\0'; // uppercase first character of name, '\0' if name is empty // length of name in characters, null-terminator excluded, 0 if the length is bigger than 255 // 在原作者的 C# 代码中是这么读取的:uint nameOffset = binaryReader.ReadUInt16() | (uint)((flags & 0xF) << 16); // 所以不能直接使用成员 bNameLength。请使用结构体中 name_offset () 方法来获取值 BYTE bNameLength = 0; BYTE bFlags = 0; // flags and upper bits of name offset WORD wNameOffset = 0; // offset of name in ASCII or Unicode name block in characters // index property // bits 0-3: upper bits 16-19 of name offset // bit 4: set if resource name is a scope, unset if it is an item // bit 5 : set if name is stored in the ASCII name block, unset if // it is stored in the Unicode name block WORD wIndexProp = 0; bool is_scope () const { return bFlags & 0x10; } bool name_in_ascii () const { return bFlags & 0x20; } DWORD name_offset () const { return (DWORD)wNameOffset | ((DWORD)(bFlags & 0xF) << 16); } DWORD index () const { return wIndexProp; } //friend istreamstream &operator >> (istreamstream &i, _SCOPE_ITEM_INFO &s) //{ // i >> s.wParentScopeIndex >> // s.wFullPathLength; // s.wchUpperFirst = i.read_bytes (sizeof (UINT16)); // s.bNameLength = i.read_bytes (); // s.bFlags = i.read_bytes (); // s.wNameOffset = i.read_bytes (); // s.wIndexProp = i.read_bytes (); // return i; //} //friend bytesstream &operator >> (bytesstream &i, _SCOPE_ITEM_INFO &s) //{ // i >> s.wParentScopeIndex >> // s.wFullPathLength; // s.wchUpperFirst = i.read_bytes (sizeof (UINT16)); // s.bNameLength = i.read_bytes (); // s.bFlags = i.read_bytes (); // s.wNameOffset = i.read_bytes (); // s.wIndexProp = i.read_bytes (); // return i; //} } SCOPE_ITEM_INFO; typedef struct _SCOPE_EX_INFO { WORD wScopeIndex = 0; // scope index WORD wChildCount = 0; // child count WORD wFirstChild = 0; // scope or item index of first child, all other children follow sequentially WORD wUnknown1 = 0; // unknown, zero //friend istreamstream &operator >> (istreamstream &i, _SCOPE_EX_INFO &e) //{ // i >> e.wScopeIndex >> // e.wChildCount >> // e.wFirstChild; // i.expect (0, true, &e.wUnknown1); // return i; //} bool valid () const { return !wUnknown1; } } SCOPE_EX_INFO; typedef WORD ITEM_INDEX; enum class RES_MAP_OBJTYPE { ENTRY = 0, SCOPE = 1, ITEM = 2 }; class RES_MAP_SCOPE; class RES_MAP_ENTRY { public: ITEM_INDEX wIndex = 0; std::wstring strName; RES_MAP_SCOPE *pParent = nullptr; RES_MAP_ENTRY (ITEM_INDEX index = 0, RES_MAP_SCOPE *parent = {}, const std::wstring &name = L"", RES_MAP_OBJTYPE type = RES_MAP_OBJTYPE::ENTRY, bool setnull = true): wIndex (index), strName (name), pParent (parent), eType (type), bIsNull (setnull) {} std::wstring full_name (WCHAR divide = L'\\'); size_t path (std::vector &output); void refresh_full_name () { strFullName.clear (); } RES_MAP_OBJTYPE type () const { return eType; } bool bIsNull = false; protected: std::wstring strFullName; RES_MAP_OBJTYPE eType; }; class RES_MAP_SCOPE: public RES_MAP_ENTRY { public: explicit RES_MAP_SCOPE (ITEM_INDEX index = 0, RES_MAP_SCOPE *parent = nullptr, const std::wstring &name = L"", bool setnull = true): RES_MAP_ENTRY (index, parent, name, RES_MAP_OBJTYPE::SCOPE, setnull) {} std::vector vecChild; }; std::wstring RES_MAP_ENTRY::full_name (WCHAR divide) { if (strFullName.empty ()) { if (pParent) strFullName = pParent->full_name (divide) + divide + strName; else strFullName = strName; } return strFullName; } size_t RES_MAP_ENTRY::path (std::vector &output) { output.clear (); if (pParent) pParent->path (output); output.push_back (strName); return output.size (); } class RES_MAP_ITEM: public RES_MAP_ENTRY { public: RES_MAP_ITEM (ITEM_INDEX index = 0, RES_MAP_SCOPE *parent = nullptr, const std::wstring &name = L"", bool setnull = true): RES_MAP_ENTRY (index, parent, name, RES_MAP_OBJTYPE::ITEM, setnull) {} }; // DecisionInfo typedef struct _DECISION_INFO { WORD wFirstQualiIndex = 0; // index of the first qualifier set index in the index table WORD wQualiSetsCount = 0; // number of qualifiers sets in decision } DECISION_INFO; typedef struct _QUALIFIER_SET_INFO { WORD wFirstQualiIndex = 0; // index of the first qualifier index in the index table // firstQualifierIndexIndex WORD wQualiSetsCount = 0; // number of qualifiers in qualifier set // numQualifiersInSet } QUALIFIER_SET_INFO; typedef struct _QUALIFIER_INFO { WORD wDistQualiIndex = 0; // index of distinct qualifier WORD wPriority = 0; // priority WORD wFallbackScore = -1; // fallback score, values range from 0 to 1000 WORD wUnknown1 = -1; // unknown, zero } QUALIFIER_INFO; enum class QUALIFIER_TYPE: WORD { LANGUAGE = 0, // 语言 (0) CONTRAST = 1, // 对比度 (1) SCALE = 2, // 比例 (2) HOMEREGION = 3, // 主屏幕区域 (3) TARGETSIZE = 4, // 目标尺寸 (4) LAYOUTDIR = 5, // 布局方向 (5) THEME = 6, // 主题 (6) ALTERNATEFORM = 7, // 替代格式 (7) DXFEATURELEVEL = 8, // DX 功能等级 (8) CONFIG = 9, // 配置 (9) DEVICEFAMILY = 10, // 设备系列 (10) CUSTOM = 11, // 自定义 (11) }; std::wstring EnumToStringW (QUALIFIER_TYPE value) { switch (value) { case QUALIFIER_TYPE::LANGUAGE: return L"Language"; break; case QUALIFIER_TYPE::CONTRAST: return L"Contrast"; break; case QUALIFIER_TYPE::SCALE: return L"Scale"; break; case QUALIFIER_TYPE::HOMEREGION: return L"HomeRegion"; break; case QUALIFIER_TYPE::TARGETSIZE: return L"TargetSize"; break; case QUALIFIER_TYPE::LAYOUTDIR: return L"LayoutDir"; break; case QUALIFIER_TYPE::THEME: return L"Theme"; break; case QUALIFIER_TYPE::ALTERNATEFORM: return L"AlternateForm"; break; case QUALIFIER_TYPE::DXFEATURELEVEL: return L"DxFeatureLevel"; break; case QUALIFIER_TYPE::CONFIG: return L"Configure"; break; case QUALIFIER_TYPE::DEVICEFAMILY: return L"DeviceFamily"; break; case QUALIFIER_TYPE::CUSTOM: return L"Custom"; break; } } typedef struct _DISTINCE_QUALIFIER_INFO { WORD wUnknown1 = 0; // unknown WORD wQualiType = 0; // qualifier type WORD wUnknown2 = 0; // unknown WORD wUnknown3 = 0; // unknown DWORD wQualiValueOffset = 0; // offset of qualifier value in qualifier value block, in characters } DISTINCE_QUALIFIER_INFO; typedef struct _QUALIFIER { ITEM_INDEX wIndex = 0; QUALIFIER_TYPE eType = QUALIFIER_TYPE::CUSTOM; WORD wPriority = 0; DOUBLE dFallbackScope = 0; std::wstring swValue = 0; _QUALIFIER (ITEM_INDEX index = 0, QUALIFIER_TYPE type = QUALIFIER_TYPE::CUSTOM, WORD priority = 0, DOUBLE fallbackScope = 0, const std::wstring &value = L""): wIndex (index), eType (type), wPriority (priority), dFallbackScope (fallbackScope), swValue (value) {} } QUALIFIER; typedef struct _QUALIFIER_SET { WORD wIndex = 0; std::vector vecQuals; _QUALIFIER_SET (WORD index = 0, const std::vector &quals = {}): wIndex (index), vecQuals (quals) {} } QUALIFIER_SET; typedef struct _DECISION { WORD wIndex = 0; std::vector verQualSets; _DECISION (WORD index, const std::vector &qualsets = {}): wIndex (index), verQualSets (qualsets) {} } DECISION; // ResourceMap & ResourceMap2 typedef struct _HSCHEMA_REF_BLOCK { HSCHEMA_VERSION_INFO verHschema; // hierarchical schema version info struct { WORD wUniIdLength = 0; // length of unique id in characters, null-terminator included WORD wUnknown1 = -1; // unknown, zero DWORD dwUnknown2 = 0; // unknown DWORD dwUnknown3 = 0; // unknown } part2; std::wstring swUniqueId = L""; // unique id bool empty () { return swUniqueId.empty () && part2.wUnknown1 != 0; } } HSCHEMA_REF_BLOCK; enum class RES_VALUE_TYPE: DWORD { STRING = 0, // String (0) PATH = 1, // Path (1) EMBEDDEDDATA = 2, // EmbeddedData (2) ASCIISTRING = 3, // AsciiString (3) UTF8STRING = 4, // Utf8String (4) ASCIIPATH = 5, // AsciiPath (5) UTF8PATH = 6 // Utf8Path (6) }; typedef struct _RES_VALUE_TYPE_TABLE { DWORD dwUnknown1 = 0; // unknown, 4 DWORD dwResType = -1; // resource value type } RES_VALUE_TYPE_TABLE; typedef struct _ITEM_ITEMINFO_GROUP_TABLE_ENTRY { WORD wFirstIndexProperty = 0; // index property of first resource item WORD wItemInfoGroupIndex = 0; // index of iteminfo group } ITEM_ITEMINFO_GROUP_TABLE_ENTRY; typedef struct _ITEMINFO_GROUP_TABLE_ENTRY { WORD wItemInfoCount; // number of iteminfos in this group WORD wFirstItemIndex; // index of the first iteminfo in this group _ITEMINFO_GROUP_TABLE_ENTRY (WORD count = 0, WORD firstIndex = 0): wItemInfoCount (count), wFirstItemIndex (firstIndex) {} } ITEMINFO_GROUP_TABLE_ENTRY; typedef struct _ITEM_ITEMINFO_TABLE_ENTRY { WORD wDecisionIndex = 0; // index of decision WORD wFirstCandiIndex = 0; // index of first candidate } ITEM_ITEMINFO_TABLE_ENTRY; typedef struct _TABLE_EXT_BLOCK { DWORD dwItemAdditEntCount = 0; // number of additional entries of the item to iteminfo group table // ItemToItemInfoGroupCountLarge DWORD dwItemGroupAdditEntCount = 0; // number of additional entries of the item info group table // itemInfoGroupCountLarge DWORD dwItemTableAdditEntCount = 0; // number of additional entries of the iteminfo table // itemInfoCountLarge } TABLE_EXT_BLOCK; typedef BYTE CANDIDATE_TYPE; typedef struct _CANDIDATE0_DATA { BYTE bResValueType = 0; // resource value type, specified as an index into the resource value type table WORD wEmbeddedLength = 0; // embedded data length DWORD dwEmbeddedOffset = 0; // offset of the embedded data in the embedded data block } CANDIDATE0_DATA; typedef struct _CANDIDATE1_DATA { BYTE bResValueType = 0; // resource value type, specified as an index into the resource value type table WORD wSrcFile = 0; // source file // sourceFileIndex WORD wDataIndex = 0; // index of the data item storing the data // valueLocation WORD wSectIndex = 0; // section index of the Data Item Section storing the data // dataItemSection } CANDIDATE1_DATA; typedef struct _CANDIDATE_INFO { BYTE bCandidateType = 0; // 0 或 1 union CANDIDATE { CANDIDATE0_DATA _0; CANDIDATE1_DATA _1; CANDIDATE (CANDIDATE0_DATA can0): _0 (can0) {} CANDIDATE (CANDIDATE1_DATA can1): _1 (can1) {} CANDIDATE () {} } objCandidate; _CANDIDATE_INFO (CANDIDATE0_DATA can0): bCandidateType (0), objCandidate (can0) {} _CANDIDATE_INFO (CANDIDATE1_DATA can1): bCandidateType (1), objCandidate (can1) {} _CANDIDATE_INFO (): bCandidateType (-1) {} bool candidate_0 () const { return bCandidateType == 0; } bool candidate_1 () const { return bCandidateType == 1; } bool valid () const { return candidate_0 () ^ candidate_1 (); } } CANDIDATE_INFO; struct basic_sect; template struct ref_sect { using classtype = ref_sect ; // static_assert (std::is_base_of ::value, "SectionType must derive from basic_sect"); int index; ref_sect (int sect_index = -1): index (sect_index) {} using sect_type = SectionType; explicit operator int () { return index; } bool valid () const { return index != -1; } void reset () { index = -1; } bool operator == (const classtype &another) const { return index == another.index; } classtype &operator = (const classtype &another) { index = another.index; return *this; } classtype &operator = (int newvalue) { index = newvalue; return *this; } }; typedef struct _RES_MAP_ITEM_REF { ref_sect wSchemaSect; int iItemIndex; _RES_MAP_ITEM_REF (const ref_sect &ssect, int itemindex): wSchemaSect (ssect), iItemIndex (itemindex) {} _RES_MAP_ITEM_REF () {} } RES_MAP_ITEM_REF; struct _CANDIDATE; typedef struct _CANDIDATE_SET { RES_MAP_ITEM_REF refResMapItem; WORD wDecisionIndex = 0; std::vector <_CANDIDATE> vecCandidates; } CANDIDATE_SET; typedef struct _BASIC_REF { INT64 llIndex = -1; bool valid () const { return llIndex >= 0; } void reset () { llIndex = -1; } void set (int index) { llIndex = index; } void setnull () { llIndex = -1; } int get () { return llIndex; } bool isnull () { return !valid (); } _BASIC_REF (int index = 0, bool isnull = false): llIndex (isnull ? -1 : index) {} operator int () { return get (); } _BASIC_REF &operator = (int index) { set (index); return *this; } _BASIC_REF &operator = (const _BASIC_REF &rfr) { this->llIndex = rfr.llIndex; return *this; } bool operator == (const _BASIC_REF &another) const { return this->llIndex == another.llIndex; } bool operator == (int index) const { return (int)this->llIndex == index; } } BASIC_REF; typedef struct _REF_FILE_REF: public BASIC_REF { using BASIC_REF::_BASIC_REF; } REF_FILE_REF; typedef struct _DATA_ITEM_REF: public BASIC_REF { ref_sect iDataSectIndex; _DATA_ITEM_REF (int itemIndex = 0, int itemDataSectIndex = 0, bool isnull = false): BASIC_REF (itemIndex, isnull), iDataSectIndex (itemDataSectIndex) {} operator int () = delete; _DATA_ITEM_REF &operator = (int) = delete; _DATA_ITEM_REF &operator = (const _BASIC_REF &) = delete; _DATA_ITEM_REF &operator = (const _DATA_ITEM_REF &another) { this->llIndex = another.llIndex; this->iDataSectIndex = another.iDataSectIndex; return *this; } bool operator == (int) = delete; bool operator == (const _BASIC_REF &) = delete; bool operator == (const _DATA_ITEM_REF &another) const { return llIndex == another.llIndex && iDataSectIndex == another.iDataSectIndex; } } DATA_ITEM_REF; typedef struct _BYTE_SPAN { ulint offset = 0; size_t length = 0; bool isnull = false; using classtype = _BYTE_SPAN; _BYTE_SPAN (ulint p_of = 0, size_t len = 0, bool nullstatus = false): offset (p_of), length (len), isnull (nullstatus) {} // 读取时会更改文件指针位置 HRESULT get_bytes (istreamstream istream, std::vector &retbytes, size_t *retbyteslen = nullptr, bool cutbytesnoread = false) { retbytes.clear (); retbytes.resize (length); size_t bytesread = 0; if (retbyteslen) *retbyteslen = 0; HRESULT hr = istream.seek (offset, istreamstream::seekpos::start); #ifdef _DEBUG auto allsize = istream.size (); #endif if (FAILED (hr)) return hr; hr = istream.read (retbytes.data (), length, &bytesread); if (retbyteslen) *retbyteslen = bytesread; if (cutbytesnoread) retbytes.resize (bytesread); return hr; } } BYTE_SPAN; typedef struct _CANDIDATE { WORD wQualifierSet = 0; RES_VALUE_TYPE dwResType = RES_VALUE_TYPE::STRING; REF_FILE_REF iSrcFileIndex = 0; DATA_ITEM_REF iDataItem = 0; BYTE_SPAN posData; _CANDIDATE (WORD qualifierSet, RES_VALUE_TYPE resType, REF_FILE_REF srcFile, DATA_ITEM_REF dataItem): wQualifierSet (qualifierSet), dwResType (resType), iSrcFileIndex (srcFile), iDataItem (dataItem), posData (0, 0, true) {} _CANDIDATE (WORD qualifierSet, RES_VALUE_TYPE resType, BYTE_SPAN data): wQualifierSet (qualifierSet), dwResType (resType), iSrcFileIndex (-1, true), iDataItem (-1, 0, true), posData (data) {} _CANDIDATE (): iSrcFileIndex (-1, true), iDataItem (-1, 0, true), posData (0, 0, true) {} // 无效会返回 nullptr REF_FILE_REF *source_file_index () { return iSrcFileIndex.valid () ? &iSrcFileIndex : nullptr; } // 无效会返回 nullptr DATA_ITEM_REF *data_item_ref () { return iDataItem.valid () ? &iDataItem : nullptr; } // 无效会返回 nullptr BYTE_SPAN *data_position () { return posData.isnull ? nullptr : &posData; } } CANDIDATE; // DataItem typedef struct _STORED_STRING_INFO { WORD wStringOffset = 0; // string offset, relative to start of stored data WORD wStringLength = 0; // string length in bytes } STORED_STRING_INFO; typedef struct _STORED_BLOB_INFO { DWORD dwBlobOffset = 0; // blob offset, relative to start of stored data DWORD dwBlobLength = 0; // blob length in bytes } STORED_BLOB_INFO; // ReferencedFile typedef struct _REF_FOLDER_INFO { WORD wUnknown1 = -1; // unknown, zero WORD wParentIndex = 0xFFFF; // index of parent folder, 0xFFFF if no parent exists (root) WORD wFolderCount = 0; // number of folders in this folder WORD wFirstFolderIndex = 0; // index of first folder in this folder WORD wFileCount = 0; // number of files in this folder WORD wFirstFileIndex = 0; // index of first file in this folder WORD wFolderNameLength = 0; // length of folder name in characters WORD wFolderFullPathLength = 0; // length of full folder path DWORD dwFolderNameOffset = 0; // offset of folder name in Unicode name block friend istreamstream &operator >> (istreamstream &i, _REF_FOLDER_INFO &reff) { return i >> reff.wUnknown1 >> reff.wParentIndex >> reff.wFolderCount >> reff.wFirstFolderIndex >> reff.wFileCount >> reff.wFirstFileIndex >> reff.wFolderNameLength >> reff.wFolderFullPathLength >> reff.dwFolderNameOffset; } friend bytesstream &operator >> (bytesstream &i, _REF_FOLDER_INFO &reff) { return i >> reff.wUnknown1 >> reff.wParentIndex >> reff.wFolderCount >> reff.wFirstFolderIndex >> reff.wFileCount >> reff.wFirstFileIndex >> reff.wFolderNameLength >> reff.wFolderFullPathLength >> reff.dwFolderNameOffset; } } REF_FOLDER_INFO; typedef struct _REF_FILE_INFO { WORD wUnknown1 = 0; // unknown WORD wParentIndex = 0; // index of parent folder WORD wFileFullPathLength = 0; // length of full file path WORD wFileNameLength = 0; // length of file name in characters DWORD dwFileNameOffset = 0; // offset of file name in Unicode name block friend istreamstream &operator >> (istreamstream &i, _REF_FILE_INFO &r) { return i >> r.wUnknown1 >> r.wParentIndex >> r.wFileFullPathLength >> r.wFileNameLength >> r.dwFileNameOffset; } friend bytesstream &operator >> (bytesstream &i, _REF_FILE_INFO &r) { return i >> r.wUnknown1 >> r.wParentIndex >> r.wFileFullPathLength >> r.wFileNameLength >> r.dwFileNameOffset; } } REF_FILE_INFO; struct _REF_FOLDER; typedef struct _REF_FILE_ENTRY { enum class ENTRYTYPE { UNKNOWN = 0, FOLDER = 1, FILE = 2 }; _REF_FOLDER *rfParent; std::wstring swName = L""; std::wstring fullname (); std::wstring refresh_fullname () { swFullName.clear (); } size_t path (std::vector &output); ENTRYTYPE type () const { return eType; } _REF_FILE_ENTRY (const std::wstring &name = L"", _REF_FOLDER *parent = nullptr, ENTRYTYPE type = ENTRYTYPE::UNKNOWN): swName (name), eType (type), rfParent (parent) {} protected: std::wstring swFullName = L""; ENTRYTYPE eType = ENTRYTYPE::UNKNOWN; } REF_FILE_ENTRY; typedef struct _REF_FOLDER: public _REF_FILE_ENTRY { std::vector vecChildrens; _REF_FOLDER (const std::wstring &name = L"", _REF_FOLDER *parent = nullptr): REF_FILE_ENTRY (name, parent, REF_FILE_ENTRY::ENTRYTYPE::FOLDER) {} } REF_FOLDER; std::wstring _REF_FILE_ENTRY::fullname () { if (std::wnstring::empty (swFullName)) { if (rfParent) swFullName = rfParent->fullname () + L"\\" + swName; else swFullName = swName; } return swFullName; } size_t _REF_FILE_ENTRY::path (std::vector &output) { output.clear (); if (rfParent) rfParent->path (output); output.push_back (swName); return output.size (); } typedef struct _REF_FILE: public REF_FILE_ENTRY { _REF_FILE (const std::wstring &name = L"", _REF_FOLDER *parent = nullptr): REF_FILE_ENTRY (name, parent, REF_FILE_ENTRY::ENTRYTYPE::FILE) {} } REF_FILE; // ReverseMap typedef struct _SCOPE_AND_ITEM_INFO { WORD wParent; WORD wFullPathLength; DWORD dwHashCode; // 不能直接使用,而是使用方法 name_offset WORD wNameOffset; WORD wIndex; DWORD name_offset () const { return (DWORD)wNameOffset | (((dwHashCode >> 24) & 0xF) << 16); } bool name_in_ascii () const { return dwHashCode & 0x20000000; } bool is_scope () const { return dwHashCode & 0x10000000; } auto Item1 () const { return wParent; } auto Item2 () const { return wFullPathLength; } auto Item3 () const { return dwHashCode; } auto Item4 () const { return name_offset (); } auto Item5 () const { return wIndex; } } SCOPE_AND_ITEM_INFO; struct basic_sect { section § basic_sect (section &s): sect (s) {} explicit operator section () { return sect; } SectionType type () const { return sect.type (); } }; struct basic_sect_func { public: virtual bool valid () = 0; virtual void reset () = 0; virtual bool parse () = 0; }; #define counter(_count_, _variable_) for (size_t _variable_ = 0, _counter_##_variable_##_total_ = _count_; _variable_ < _counter_##_variable_##_total_; _variable_ ++) struct sect_pridesp: public basic_sect, public basic_sect_func { sect_pridesp (section &s): basic_sect (s) { if (s.type () != SectionType::PriDescriptor) throw std::exception ("Error: Section type error."); parse (); } struct ContentStruct { WORD wFlags = 0; // 标志 / flags WORD wIncFileListIndex = -1; // 包含文件列表节(Included File List)索引,若不存在则为 0xFFFF WORD wUnknown1 = -1; // 未知,0 WORD wHieraScheCount = 0; // Hierarchical Schema 节数量 WORD wDecInfoCount = 0; // Decision Info 节数量 WORD wResMapCount = 0; // Resource Map 节数量 WORD wResMapBegIndex = -1; // 主资源映射(primary resource map)的节索引,若无则 0xFFFF WORD wRefFileCount = 0; // Referenced File 节数量 WORD wDataItemCount = 0; // Data Item 节数量 WORD wUnknown2 = -1; // 未知,0 ContentStruct () = default; ContentStruct (WORD f, WORD inc, WORD unk1, WORD hiera, WORD dec, WORD res, WORD resBeg, WORD ref, WORD data, WORD unk2) : wFlags (f), wIncFileListIndex (inc), wUnknown1 (unk1), wHieraScheCount (hiera), wDecInfoCount (dec), wResMapCount (res), wResMapBegIndex (resBeg), wRefFileCount (ref), wDataItemCount (data), wUnknown2 (unk2) {} } content; std::vector > vec_ref_hs; std::vector > vec_ref_deci; std::vector > vec_ref_rm; std::vector > vec_ref_rf; std::vector > vec_ref_dati; ref_sect primary_resmap; bool valid () { return content.wUnknown1 == 0 && content.wUnknown2 == 0; } void reset () { vec_ref_hs.clear (); vec_ref_deci.clear (); vec_ref_rm.clear (); vec_ref_rf.clear (); vec_ref_dati.clear (); primary_resmap.reset (); content = {0, (WORD)-1, (WORD)-1, 0, 0, 0, (WORD)-1, 0, 0, (WORD)-1}; } bool parse () { reset (); HRESULT hr = sect.childst.seek (); istreamstream fp (sect.childst.ifile); DWORD dwContent = 0; hr = sect.childst.ifile->Read (&content, sizeof (content), &dwContent); if (!valid ()) return false; if (content.wResMapBegIndex != 0xFFFF) primary_resmap.index = content.wResMapBegIndex; counter (content.wHieraScheCount, i) { vec_ref_hs.push_back (fp.read_bytes ()); } counter (content.wDecInfoCount, i) { vec_ref_deci.push_back (fp.read_bytes ()); } counter (content.wResMapCount, i) { vec_ref_rm.push_back (fp.read_bytes ()); } counter (content.wRefFileCount, i) { vec_ref_rf.push_back (fp.read_bytes ()); } counter (content.wDataItemCount, i) { vec_ref_dati.push_back (fp.read_bytes ()); } return true; } }; struct sect_hierasche: public basic_sect, public basic_sect_func { struct { struct { WORD wUnknown1 = 0; // 未知,1 WORD wUniqRMNameLen = 0; // 资源映射唯一名长度(字符数,含终止符) WORD wResMapNameLen = 0; // 资源映射名称长度(字符数,含终止符) WORD wUnknown2 = -1; // unknown, zero } part1; struct { // hname 标识符(仅 extended 存在) // hname identifier: only present in the extended Hierarchical Schema Section. // Observed values are "[def_hnames] \0" and "[def_hnamesx] \0". CHAR szHNameExt [16] = {0}; } part2; struct { HSCHEMA_VERSION_INFO verSchema; // 层次化 schema 版本信息 } part3; struct { std::wstring swUniqueRMName = L""; // 资源映射的唯一名(unique name) std::wstring swResMapName = L""; // name of resource map WORD wUnknown3 = -1; // unknown, zero WORD wMaxFullPathLength = 0; // length of longest full path of all resource names WORD wUnknown3_5 = -1; // unknown, zero DWORD dwResNameCount = 0; // number of resource names, usually number of scopes + items DWORD dwScopeCount = 0; // number of scopes DWORD dwItemsCount = 0; // number of items DWORD dwUniNameLemgth = 0; // length of Unicode name block DWORD dwUnknown4 = 0; // unknown // unknown at 70 + ?: only present in the extended Hierarchical // Schema Section and if hname identifier is "[def_hnamesx] \0". DWORD dwUnknown5 = 0; void *get_buf_first_dir () { return &wUnknown3; } size_t get_buf_size_of () { return sizeof (wUnknown3) + sizeof (wMaxFullPathLength) + sizeof (wUnknown3_5) + sizeof (dwScopeCount) + sizeof (dwItemsCount) + sizeof (dwResNameCount) + sizeof (dwUniNameLemgth) + sizeof (dwUnknown4) + sizeof (dwUnknown5); } } part4; } content; BOOL ex = FALSE; BOOL exHName = FALSE; std::vector vec_scope_and_items; std::vector vec_scope_ex; std::vector vec_item_index; std::vector vec_scopes; std::vector vec_items; sect_hierasche (section &s): basic_sect (s) { if (s.type () != SectionType::HierarchicalSchema && s.type () != SectionType::HierarchicalSchemaEx) throw std::exception ("Error: Section type error."); if (s.type () == SectionType::HierarchicalSchemaEx) ex = TRUE; } void throwexpect (const std::string &reason = "Error: unexpected value.") { throw std::exception (reason.c_str ()); } bool valid () { return content.part1.wUnknown1 == 1 && content.part1.wUnknown2 == 0 && content.part3.verSchema.dwUnknown1 == 0 && content.part4.wUnknown3 == 0 && content.part4.wUnknown3_5 == 0 && content.part4.dwResNameCount == content.part3.verSchema.dwScopeCount + content.part3.verSchema.dwItemCount && content.part4.dwScopeCount == content.part3.verSchema.dwScopeCount && content.part4.dwItemsCount == content.part3.verSchema.dwItemCount && content.part4.dwUniNameLemgth == content.part3.verSchema.dwItemCount && content.part4.swUniqueRMName.length () == content.part1.wUniqRMNameLen && content.part4.swResMapName.length () == content.part1.wResMapNameLen - 1; } void reset () { vec_scope_and_items.clear (); vec_scope_ex.clear (); vec_item_index.clear (); vec_items.clear (); vec_scopes.clear (); ZeroMemory (&content.part1, sizeof (content.part1)); content.part1.wUnknown2 = -1; ZeroMemory (&content.part2, sizeof (content.part2)); ZeroMemory (&content.part3, sizeof (content.part3)); content.part3.verSchema.dwUnknown1 = -1; content.part4.swUniqueRMName = L""; content.part4.swResMapName = L""; ZeroMemory (content.part4.get_buf_first_dir (), content.part4.get_buf_size_of ()); content.part4.wUnknown3 = -1; content.part4.wUnknown3_5 = -1; } bool parse () { reset (); sect.childst.seek (); istreamstream fp (sect.childst.ifile); if (sect.childst.size == 0) return true; auto &uniqueNameLength = content.part1.wUniqRMNameLen; auto &nameLength = content.part1.wResMapNameLen; fp >> content.part1; if (content.part1.wUnknown1 != 1 || content.part1.wUnknown2 != 0) throwexpect (); auto &extendedVersion = ex; if (ex) { fp >> content.part2; if (pri_sectid (content.part2.szHNameExt, 16).equals ("[def_hnamesx]")) exHName = true; else if (pri_sectid (content.part2.szHNameExt, 16).equals ("[def_hnames]")) exHName = false; else return false; } else exHName = false; auto &extendedHNames = exHName; auto &majorVersion = content.part3.verSchema.wMajor; auto &minorVersion = content.part3.verSchema.wMinor; auto &checksum = content.part3.verSchema.dwCheckSum; auto &numScopes = content.part3.verSchema.dwScopeCount; auto &numItems = content.part3.verSchema.dwItemCount; fp >> content.part3; if (content.part3.verSchema.dwUnknown1 != 0) throwexpect (); auto &Version = content.part3.verSchema; auto &UniqueName = content.part4.swUniqueRMName; auto &Name = content.part4.swResMapName; content.part4.swUniqueRMName = fp.read_string_endwith_null_w (); content.part4.swResMapName = fp.read_string_endwith_null_w (); fp.expect (0, true, &content.part4.wUnknown3); auto &maxFullPathLength = content.part4.wMaxFullPathLength; fp >> content.part4.wMaxFullPathLength; fp.expect (0, true, &content.part4.wUnknown3_5); fp.expect (numScopes + numItems, true, &content.part4.dwResNameCount); fp.expect (numScopes, true, &content.part4.dwScopeCount); fp.expect (numItems, true, &content.part4.dwItemsCount); auto &unicodeDataLength = content.part4.dwUniNameLemgth; fp >> unicodeDataLength; fp >> content.part4.dwUnknown4; if (extendedHNames) fp >> content.part4.dwUnknown5; auto &scopeAndItemInfos = vec_scope_and_items; scopeAndItemInfos.resize (content.part4.dwResNameCount); fp.read (scopeAndItemInfos.data (), (content.part4.dwResNameCount) * sizeof (SCOPE_ITEM_INFO)); auto &scopeExInfos = vec_scope_ex; scopeExInfos.resize (numScopes); fp.read (scopeExInfos.data (), numScopes * sizeof (SCOPE_EX_INFO)); auto &itemIndexPropertyToIndex = vec_item_index; itemIndexPropertyToIndex.resize (numItems); fp.read (itemIndexPropertyToIndex.data (), sizeof (ITEM_INDEX) * numItems); auto unicodeDataOffset = fp.position (); auto asciiDataOffset = fp.position () + unicodeDataOffset * 2; auto &scopes = vec_scopes; auto &items = vec_items; scopes.resize (numScopes); items.resize (numItems); counter (content.part4.dwResNameCount, i) { istreamstream::fsize_t pos = 0; if (scopeAndItemInfos [i].name_in_ascii ()) pos = asciiDataOffset + scopeAndItemInfos [i].name_offset (); else pos = unicodeDataOffset + scopeAndItemInfos [i].name_offset () * 2; fp.seek (pos, istreamstream::seekpos::start); std::wstring name; if (scopeAndItemInfos [i].wFullPathLength != 0) { if (scopeAndItemInfos [i].name_in_ascii ()) name = StringToWString (fp.read_string_endwith_null_a ()); else name = fp.read_string_endwith_null_w (); } ITEM_INDEX index = scopeAndItemInfos [i].index (); if (scopeAndItemInfos [i].is_scope ()) { if (!scopes [index].bIsNull) throwexpect (); else scopes.at (index) = RES_MAP_SCOPE (index, nullptr, name, false); } else { if (!items [index].bIsNull) throwexpect (); else items.at (index) = RES_MAP_ITEM (index, nullptr, name, false); } } counter (content.part4.dwResNameCount, i) { ITEM_INDEX index = scopeAndItemInfos [i].index (); WORD parent = scopeAndItemInfos [scopeAndItemInfos [i].wParentScopeIndex].index (); if (parent != 0xFFFF) { if (scopeAndItemInfos [i].is_scope ()) { if (parent != index) scopes.at (index).pParent = &scopes [parent]; } else items.at (index).pParent = &scopes [parent]; } } counter (numScopes, i) { auto &scope = scopes [i]; auto &children = scope.vecChild; counter (scopeExInfos [i].wChildCount, j) { auto &saiInfo = scopeAndItemInfos [scopeExInfos [i].wFirstChild + j]; if (saiInfo.is_scope ()) children.push_back (&scopes.at (saiInfo.index ())); else children.push_back (&items.at (saiInfo.index ())); } } return valid (); } }; struct sect_decinfo: public basic_sect, public basic_sect_func { sect_decinfo (section &s): basic_sect (s) { if (s.type () != SectionType::DecisionInfo) throw std::exception ("Error: Section type error."); } struct { WORD wDistQualiCount = 0; // 不同的 distinct qualifiers 数量 / number of distinct qualifiers WORD wQualifierCount = 0; // qualifiers 数量 WORD wQualSetsCount = 0; // qualifier sets 数量 WORD wDecisionCount = 0; // decisions 数量 / number of decisions WORD wEntriesCount = 0; // index table 条目数 / number of entries in the index table WORD wQualiValueLength = 0; // qualifier value block 长度(字符数) / length of qualifier value block in characters } content; std::vector vec_qua_set; std::vector vec_qua; std::vector vec_dec; bool valid () { return content.wDecisionCount || content.wQualifierCount || content.wDistQualiCount || content.wQualSetsCount || content.wEntriesCount || content.wQualiValueLength || vec_qua.size () || vec_qua_set.size () || vec_dec.size () || 0; } void reset () { vec_qua.clear (); vec_qua_set.clear (); vec_dec.clear (); ZeroMemory (&content, sizeof (content)); } bool parse () { reset (); istreamstream fp (sect.childst.ifile); sect.childst.seek (); auto &numDistinctQualifiers = content.wDistQualiCount; auto &numQualifiers = content.wQualifierCount; auto &numQualifierSets = content.wQualSetsCount; auto &numDecisions = content.wDecisionCount; auto &numIndexTableEntries = content.wEntriesCount; auto &totalDataLength = content.wQualiValueLength; fp >> content; std::vector decisionInfos (numDecisions); std::vector qualifierSetInfos (numQualifierSets); std::vector qualifierInfos (numQualifiers); std::vector distinctQualifierInfos (numDistinctQualifiers); std::vector indexTable (numIndexTableEntries); auto &vec_dis_qua_info = distinctQualifierInfos; auto &vec_qua_info = qualifierInfos; auto &vec_qua_set_info = qualifierSetInfos; auto &indexs = indexTable; auto &vec_dec_info = decisionInfos; fp.read (decisionInfos.data (), sizeof (DECISION_INFO) * numDecisions); fp.read (qualifierSetInfos.data (), sizeof (DECISION_INFO) * numQualifierSets); fp.read (qualifierInfos.data (), sizeof (QUALIFIER_INFO) * numQualifiers); for (auto &it : qualifierInfos) { if (it.wUnknown1 != 0) throw std::exception ("Error: unexpective value."); } fp.read (distinctQualifierInfos.data (), sizeof (DISTINCE_QUALIFIER_INFO) * numDistinctQualifiers); fp.read (indexTable.data (), sizeof (ITEM_INDEX) * numIndexTableEntries); auto currentpos = fp.position (); std::vector buf (128); fp.read (buf.data (), 128 * sizeof (WCHAR)); fp.seek (currentpos, istreamstream::seekpos::start); counter (content.wQualifierCount, i) { auto &dinfo = vec_dis_qua_info [vec_qua_info [i].wDistQualiIndex]; auto &qinfo = vec_qua_info [i]; fp.seek (currentpos + dinfo.wQualiValueOffset * 2, istreamstream::seekpos::start); std::wstring value = fp.read_string_endwith_null_w (); QUALIFIER qual (i, (QUALIFIER_TYPE)dinfo.wQualiType, qinfo.wPriority, (double)qinfo.wFallbackScore * 0.001, value); vec_qua.push_back (qual); } counter (content.wQualSetsCount, i) { std::vector quals; auto qset = vec_qua_set_info [i]; counter (qset.wQualiSetsCount, j) { auto &ind = indexs [qset.wFirstQualiIndex + j]; auto &qual = vec_qua [ind]; quals.push_back (qual); } vec_qua_set.emplace_back (QUALIFIER_SET (i, quals)); } counter (content.wDecisionCount, i) { auto &dec = vec_dec_info [i]; std::vector qsets; counter (dec.wQualiSetsCount, j) { auto &ind = indexs [dec.wFirstQualiIndex + j]; auto &qset = vec_qua_set.at (ind); qsets.emplace_back (qset); } vec_dec.emplace_back (DECISION (i, qsets)); } return valid (); } }; struct sect_resmap: public basic_sect, public basic_sect_func { sect_resmap (section &s): basic_sect (s) { if (s.type () != SectionType::ResourceMap && s.type () != SectionType::ResourceMap2) throw std::exception ("Error: Section type error."); if (s.type () == SectionType::ResourceMap2) ver2 = true; else ver2 = false; } struct { WORD wEnvRefLength = 0; // length of environment references block // environmentReferencesLength WORD wRefCount = 0; // number of references in environment references block // numEnvironmentReferences WORD wHSSectIndex = 0; // section index of Hierarchical Schema Section // SchemaSection WORD wHSRefLength = 0; // length of hierarchical schema reference block // hierarchicalSchemaReferenceLength WORD wDecInfSectIndex = 0; // section index of Decision Info Section // DecisionInfoSection WORD wResTypeEntCount = 0; // number of entries in resource value type table // resourceValueTypeTableSize WORD wItemEntCount = 0; // number of entries in item to iteminfo group table // ItemToItemInfoGroupCount WORD wItemGroupEntCount = 0; // number of entries in iteminfo group table // itemInfoGroupCount DWORD dwItemTableEntCount = 0; // number of entries in iteminfo table // itemInfoCount DWORD dwCandidateCount = 0; // number of candidates // numCandidates DWORD dwEmbededDataCount = 0; // length of embedded data bloc // dataLength DWORD dwTableExtCount = 0; // length of table extension block // largeTableLength } content; BOOL ver2 = FALSE; std::vector bvecEnvRefData; std::vector bvecScheRefData; HSCHEMA_REF_BLOCK hschema_ref; std::vector vecResTypes; std::vector vecItemToItemInfoGroup; std::vector vecItemInfoGroups; std::vector vecItemInfo; std::vector vecCandidateInfo; std::map mapCandidateSet; bool valid () { if (parseError) return false; UINT64 *p = (UINT64 *)&content; bool res = false; res = (!ver2) ? (content.wEnvRefLength != 0 && content.wRefCount != 0) : (content.wEnvRefLength == 0 && content.wRefCount == 0); if (!res) return false; if (content.wHSRefLength != 0) { if (hschema_ref.verHschema.dwUnknown1 != 0) return false; if (hschema_ref.part2.wUnknown1 != 0) return false; } return res; } void reset () { parseError = false; bvecEnvRefData.clear (); bvecScheRefData.clear (); vecResTypes.clear (); vecItemToItemInfoGroup.clear (); vecItemInfoGroups.clear (); vecItemInfo.clear (); vecCandidateInfo.clear (); UINT64 *p = (UINT64 *)&content; bool res = false; size_t len = sizeof (content) / sizeof (UINT64); for (size_t i = 0; i < len; i ++) p [i] = 0; hschema_ref = HSCHEMA_REF_BLOCK (); } bool parse (); private: BOOL parseError = false; }; struct sect_dataitem: public basic_sect { sect_dataitem (section &s): basic_sect (s) { if (s.type () != SectionType::DataItem) throw std::exception ("Error: Section type error."); } struct { DWORD dwUnknown1 = -1; // unknown, zero WORD wStrCount = 0; // number of stored strings WORD wBlobCount = 0; // number of stored blobs DWORD dwStoredLength = 0; // total length of stored data } content; std::vector vecDataItems; bool valid () { return content.dwUnknown1 == 0; } void reset () { vecDataItems.clear (); ZeroMemory (&content, sizeof (content)); content.dwUnknown1 = -1; } bool parse () { reset (); sect.childst.seek (); istreamstream fp (sect.childst.ifile); auto sectionPosition = fp.position (); fp >> content; std::vector &dataItems = vecDataItems; istreamstream::fsize_t dataStartOffset = fp.position () + content.wStrCount * 2 * sizeof (WORD) + content.wBlobCount * 2 * sizeof (DWORD); dataItems.reserve (content.wStrCount + content.wBlobCount); std::vector storedStringInfos (content.wStrCount); std::vector storedBlobInfo (content.wBlobCount); fp.read (storedStringInfos.data (), sizeof (STORED_STRING_INFO) * content.wStrCount); fp.read (storedBlobInfo.data (), sizeof (STORED_BLOB_INFO) * content.wBlobCount); size_t cnt = 0; counter (content.wStrCount, i) { auto &sstr = storedStringInfos.at (i); dataItems.push_back (BYTE_SPAN (dataStartOffset + sstr.wStringOffset, sstr.wStringLength)); } counter (content.wBlobCount, i) { auto &sblo = storedBlobInfo.at (i); dataItems.push_back (BYTE_SPAN (dataStartOffset + sblo.dwBlobOffset, sblo.dwBlobLength)); } return valid (); } }; struct sect_reffile: public basic_sect { sect_reffile (section &s): basic_sect (s) { if (s.type () != SectionType::ReferencedFile) throw std::exception ("Error: Section type error."); } struct { WORD wRootCount = 0; // number of roots WORD wFolderCount = 0; // number of folders WORD wFileCount = 0; // number of folders WORD wUnknown1 = -1; // unknown, zero DWORD dwNameLength = 0; // length of Unicode name block in characters } content; std::vector vecFolderInfo; std::vector vecFileInfo; std::vector vecRefFolders; std::vector vecRefFiles; bool valid () { return content.wUnknown1 == 0; } void reset () { vecFolderInfo.clear (); vecFileInfo.clear (); vecRefFiles.clear (); vecRefFolders.clear (); ZeroMemory (&content, sizeof (content)); content.wUnknown1 = -1; } bool parse () { reset (); sect.childst.seek (); istreamstream fp (sect.childst.ifile); fp >> content; counter (content.wFolderCount, i) { REF_FOLDER_INFO folder; fp >> folder; if (folder.wUnknown1 != 0) throw std::exception ("Error: cannot get valid data in ReferencedFile Section."); vecFolderInfo.push_back (folder); } counter (content.wFileCount, i) { REF_FILE_INFO file; fp >> file; vecFileInfo.push_back (file); } auto dataStartPosition = fp.position (); auto &referencedFolders = vecRefFolders; using seekpos = istreamstream::seekpos; counter (content.wFolderCount, i) { fp.seek (dataStartPosition + vecFolderInfo [i].dwFolderNameOffset * 2, seekpos::start); std::wstring name = fp.read_string_w (vecFolderInfo [i].wFolderNameLength); referencedFolders.push_back (REF_FOLDER (name)); } counter (content.wFolderCount, i) { if (vecFolderInfo [i].wParentIndex != 0xFFFF) { referencedFolders [i].rfParent = &referencedFolders [vecFolderInfo [i].wParentIndex]; } } counter (content.wFileCount, i) { REF_FILE file; auto &fileInfo = vecFileInfo [i]; fp.seek (dataStartPosition + fileInfo.dwFileNameOffset * 2, seekpos::start); std::wstring name = fp.read_string_w (fileInfo.wFileNameLength); file.swName = name; REF_FOLDER *parent = nullptr; if (vecFileInfo [i].wParentIndex != 0xFFFF) parent = &referencedFolders [fileInfo.wParentIndex]; file.rfParent = parent; vecRefFiles.push_back (file); } counter (content.wFolderCount, i) { auto &folderInfo = vecFolderInfo [i]; auto &referencedFolder = referencedFolders [i]; counter (folderInfo.wFolderCount, j) { auto &folder = referencedFolders [folderInfo.wFirstFolderIndex + j]; referencedFolder.vecChildrens.push_back (&folder); } counter (folderInfo.wFileCount, j) { auto &file = vecRefFiles [folderInfo.wFirstFileIndex + j]; referencedFolder.vecChildrens.push_back (&file); } } return valid (); } }; struct sect_revmap: public basic_sect, public basic_sect_func { sect_revmap (section &s): basic_sect (s) { if (s.type () != SectionType::ReverseMap) throw std::exception ("Error: Section type error."); } struct { struct { DWORD dwItemsNumber = 0; DWORD dwCheckCode = 0; } part1; struct { WORD wFullPathLength = 0; WORD wUnknown1 = -1; // 0 DWORD dwEntries = 0; DWORD dwScopes = 0; DWORD dwCheckItemsNumber = 0; DWORD dwUnicodeDataLength = 0; DWORD dwSkipPadding = 0; } part2; } content; std::vector adwMap; std::vector aobjScopeAndItem; std::vector aobjScopeExts; std::vector awItemIndexs; std::vector aobjScopes; std::vector aobjItems; bool valid () { bool res = content.part2.wUnknown1 == 0 && content.part2.dwCheckItemsNumber == content.part1.dwItemsNumber || 0; return res; } void reset () { adwMap.clear (); aobjScopeAndItem.clear (); aobjScopeExts.clear (); awItemIndexs.clear (); aobjScopes.clear (); aobjItems.clear (); ZeroMemory (&content.part1, sizeof (content.part1)); ZeroMemory (&content.part2, sizeof (content.part2)); content.part2.wUnknown1 - 1; } bool parse () { reset (); sect.childst.seek (); istreamstream fp (sect.childst.ifile); fp >> content.part1; bool chk = content.part1.dwCheckCode == fp.size () - 8; if (!chk) return false; adwMap.resize (content.part1.dwItemsNumber); fp.read (adwMap.data (), sizeof (DWORD) * content.part1.dwItemsNumber); fp >> content.part2; chk = content.part2.wUnknown1 == 0 && content.part2.dwCheckItemsNumber == content.part1.dwItemsNumber; if (!chk) return false; counter (content.part2.dwScopes + content.part1.dwItemsNumber, i) { SCOPE_AND_ITEM_INFO sii; fp >> sii; aobjScopeAndItem.push_back (sii); } counter (content.part2.dwScopes, i) { SCOPE_EX_INFO sei; fp >> sei; if (sei.wUnknown1 != 0) throw std::exception ("Error: read invalid data in ReverseMap Section."); aobjScopeExts.push_back (sei); } awItemIndexs.resize (content.part1.dwItemsNumber); fp.read (awItemIndexs.data (), content.part1.dwItemsNumber * sizeof (ITEM_INDEX)); auto unicodeDataOffset = fp.position (), asciiDataOffset = fp.position () + content.part2.dwUnicodeDataLength * 2; aobjScopes.resize (content.part2.dwScopes); aobjItems.resize (content.part1.dwItemsNumber); counter (content.part1.dwItemsNumber + content.part2.dwScopes, i) { auto &sii = aobjScopeAndItem [i]; bool nameInAscii = sii.name_in_ascii (); UINT64 pos = (nameInAscii ? asciiDataOffset : unicodeDataOffset) + (sii.Item4 () * (nameInAscii ? 1 : 2)); fp.seek (pos, istreamstream::seekpos::start); std::wstring name; if (sii.Item2 ()) { if (nameInAscii) name = StringToWString (fp.read_string_endwith_null_a ()); else name = fp.read_string_endwith_null_w (); } auto index = sii.Item5 (); bool isScope = sii.is_scope (); if (isScope) { auto &it = aobjScopes.at (index); if (!it.bIsNull) throw std::exception ("Error: invalid scope data in ReverseMap Section."); else it = RES_MAP_SCOPE (index, nullptr, name); } else { auto &it = aobjItems.at (index); if (!it.bIsNull) throw std::exception ("Error: invalid item data in ReverseMap Section."); else it = RES_MAP_ITEM (index, nullptr, name); } } counter (content.part1.dwItemsNumber + content.part2.dwScopes, i) { auto &sii = aobjScopeAndItem [i]; auto index = sii.Item5 (); bool isScope = sii.is_scope (); auto parent = aobjScopeAndItem [sii.Item1 ()].Item5 (); if (parent != 0xFFFF) { if (isScope && parent != index) { auto &it = aobjScopes.at (index); it.pParent = &aobjScopes.at (parent); } else { auto &it = aobjItems.at (index); it.pParent = &aobjScopes.at (parent); } } } counter (content.part2.dwScopes, i) { auto &sei = aobjScopeExts [i]; auto &scope = aobjScopes [i]; counter (sei.wChildCount, j) { auto &saiInfo = aobjScopeAndItem [sei.wFirstChild + j]; bool isScope = saiInfo.is_scope (); if (isScope) { auto &prt = aobjScopes [saiInfo.Item5 ()]; scope.vecChild.push_back (&prt); } else { auto &prt = aobjItems [saiInfo.Item5 ()]; scope.vecChild.push_back (&prt); } } } return true; } }; struct sect_unknown: public basic_sect { sect_unknown (section &s): basic_sect (s) {} UINT64 dwLength = 0; void reset () { dwLength = 0; } bool parse () { reset (); sect.childst.seek (); istreamstream fp (sect.childst.ifile); dwLength = fp.size () - fp.position (); return true; } size_t bytes (std::vector &bytes) { sect.childst.seek (); istreamstream fp (sect.childst.ifile); bytes.resize (dwLength); size_t readlen = 0; fp.read (bytes.data (), dwLength, &readlen); bytes.resize (readlen > dwLength ? dwLength : readlen); return readlen; } }; } class prifile { private: IStream *pfile = nullptr; head header; foot footer; std::vector toclist; std::vector
sectlist; enum class searchtype { unknown, string, file }; struct search_key { std::wnstring key = L""; searchtype type = searchtype::unknown; search_key (const std::wstring &k = L"", searchtype t = searchtype::unknown): key (k), type (t) {} bool operator == (const search_key &another) const { return key == another.key && type == another.type; } bool operator == (const std::wstring &an_key) const { return key.equals (an_key); } operator searchtype () const { return type; } operator LPCWSTR () const { return key.c_str (); } }; struct search_value { std::wstring value = L""; bool isfind = false; int begindex = -1; bool finishsearch = false; operator LPCWSTR () const { return value.c_str (); } operator bool () const { return isfind; } }; std::map vecTaskSearch; bool isrunningtask = false; public: void close () { header = head (); footer = foot (); toclist.clear (); sectlist.clear (); } bool load (IStream *ifile) { close (); if (!ifile) return false; ifile->Seek (lint (0), STREAM_SEEK_SET, nullptr); DWORD dwhead = 0, dwfoot = 0; ifile->Read (&header, sizeof (header), &dwhead); if (!dwhead) return false; if (!header.valid ()) return false; ifile->Seek (lint (header.dwFileSize - 16), STREAM_SEEK_SET, nullptr); ifile->Read (&footer, sizeof (footer), &dwfoot); if (!dwfoot) return false; if (!footer.valid (header)) return false; pfile = ifile; inittoc (); initsect (); return true; } operator istreamstream () { return istreamstream (pfile); } pri::sect_pridesp section_pri_descriptor () { for (auto &it : sectlist) { if (it.type () == SectionType::PriDescriptor) { try { auto sect = pri::sect_pridesp (it); sect.parse (); return sect; } catch (const std::exception &e) { continue; } } } throw std::exception ("Error: cannot get the pri descriptor section."); } section &get_section_by_ref (int index) { return sectlist.at (index); } template SectionT get_section_by_ref (pri::ref_sect ref) { return SectionT (sectlist.at (ref.index)); } void inittoc () { toclist.clear (); pfile->Seek (lint (header.dwToCOffset), STREAM_SEEK_SET, nullptr); for (size_t i = 0; i < header.wSectCount; i ++) { tocentry toc; DWORD dwRead; pfile->Read (&toc, sizeof (toc), &dwRead); toclist.push_back (toc); } } void initsect () { sectlist.clear (); istreamstream iss (pfile); for (size_t i = 0; i < header.wSectCount; i ++) { iss.seek (header.dwSectStartOffset + toclist [i].dwSectOffset, istreamstream::seekpos::start); section sect (this->pfile, *this); iss.read (sect.head); iss.seek (sect.head.dwLength - 16 - 24); iss.read (sect.foot); iss.seek (header.dwSectStartOffset + toclist [i].dwSectOffset, istreamstream::seekpos::start); iss.seek (32); sect.childst.set (iss.position (), sect.head.dwLength - 16 - 24); sectlist.push_back (sect); } } // 用法: // auto rmsect = this->get_resmap_sect_by_ref (candidateSet.refResMapItem); // rmsect.parse (); // auto item = rmsect.vec_items [candidateSet.refResMapItem.iItemIndex]; auto get_resmap_sect_by_ref (pri::RES_MAP_ITEM_REF resourceMapItemRef) { return get_section_by_ref (resourceMapItemRef.wSchemaSect); } // 用法: // auto ds = get_dataitem_sect_by_ref (dataItemRef); // ds.parse (); // ds.vecDataItems.at (dataItemRef.get ()); auto get_dataitem_sect_by_ref (pri::DATA_ITEM_REF dataItemRef) { return get_section_by_ref (dataItemRef.iDataSectIndex); } bool get_reffile_by_ref (pri::REF_FILE_REF referencedFileRef, std::function callback) { try { auto sect = get_section_by_ref (section_pri_descriptor ().vec_ref_rf.front ()); auto &rf = sect.vecRefFiles.at (referencedFileRef); if (callback) callback (rf); return true; } catch (const std::exception &e) { return false; } } void end_taskrunning () { isrunningtask = false; } static void search_task (prifile &priinst) { destruct ([&priinst] () { priinst.end_taskrunning (); }); if (priinst.isrunningtask) return; else priinst.isrunningtask = true; auto &tasklist = priinst.vecTaskSearch; } void across_all (std::wostream &out) { #ifdef _CONSOLE struct loadingamine { const WCHAR *charcollect = L"-\\|/-\\|/"; WCHAR nowchar = L' '; bool isend = false; bool enablecallback = true; std::function callback = nullptr; void exectask () { size_t cnt = 0; size_t charlen = lstrlenW (charcollect); std::function cb = callback; while (!isend) { nowchar = charcollect [(cnt ++) % charlen]; if (cb && enablecallback) cb (nowchar); std::this_thread::sleep_for (std::chrono::milliseconds (300)); } } void run () { std::thread th (&loadingamine::exectask, this); th.detach (); } void jump () { isend = true; } } loadchar; destruct endt ([&loadchar] () { loadchar.isend = true; }); std::wcout << L" 0 %"; loadchar.callback = [] (const WCHAR &wch) { wprintf (L"\r %c 0 %%", wch); }; loadchar.run (); auto DiffSystemTimeMs = [] (const SYSTEMTIME &st1, const SYSTEMTIME &st2) -> LONGLONG { FILETIME ft1, ft2; ULARGE_INTEGER t1, t2; SystemTimeToFileTime (&st1, &ft1); SystemTimeToFileTime (&st2, &ft2); t1.LowPart = ft1.dwLowDateTime; t1.HighPart = ft1.dwHighDateTime; t2.LowPart = ft2.dwLowDateTime; t2.HighPart = ft2.dwHighDateTime; // FILETIME 单位是 100 纳秒(1秒 = 10,000,000) LONGLONG diff100ns = t2.QuadPart - t1.QuadPart; // 转换为毫秒 return diff100ns / 10000; // 1 毫秒 = 10,000 * 100ns }; out << L"Read Start: "; WCHAR buf [64]; SYSTEMTIME st_start; GetLocalTime (&st_start); swprintf (buf, 64, L"%4d.%02d.%02d %02d:%02d:%02d", st_start.wYear, st_start.wMonth, st_start.wDay, st_start.wHour, st_start.wMinute, st_start.wSecond); out << buf << std::endl; out << L"Sections: " << sectlist.size () << std::endl; for (auto &it : sectlist) { out << L" " << it << std::endl; } out << std::endl; auto pri_desp = this->section_pri_descriptor (); loadchar.callback = nullptr; loadchar.enablecallback = false; out << L"Candidates: " << pri_desp.vec_ref_rm.size () << std::endl; size_t cnt_i = 0; for (auto &it : pri_desp.vec_ref_rm) { auto resmap_sect = this->get_section_by_ref (it); resmap_sect.parse (); if (!resmap_sect.hschema_ref.empty ()) continue; auto decisionInfoSection = get_section_by_ref (resmap_sect.content.wDecInfSectIndex); decisionInfoSection.parse (); size_t cnt_j = 0; for (auto &it_cs : resmap_sect.mapCandidateSet) { auto &candidateSet = it_cs.second; auto rmsect = this->get_resmap_sect_by_ref (candidateSet.refResMapItem); rmsect.parse (); auto item = rmsect.vec_items [candidateSet.refResMapItem.iItemIndex]; out << L" " << item.full_name () << std::endl; size_t cnt_k = 0; for (auto &candidate : candidateSet.vecCandidates) { std::wstring value; if (candidate.source_file_index ()) { auto temp = get_reffile_by_ref (*candidate.source_file_index (), [&value] (pri::REF_FILE &rf) { value += L""; }); } else { pri::BYTE_SPAN byteSpan; if (candidate.data_item_ref ()) { auto dis = this->get_dataitem_sect_by_ref (*candidate.data_item_ref ()); dis.parse (); byteSpan = dis.vecDataItems.at (candidate.data_item_ref ()->get ()); } else { if (candidate.data_position ()) byteSpan = *candidate.data_position (); else byteSpan.isnull = true; } std::vector bytes (byteSpan.length + 2); size_t ret; HRESULT hr = byteSpan.get_bytes (pfile, bytes, &ret); bytes.resize (bytes.size () + 2); using restype = pri::RES_VALUE_TYPE; switch (candidate.dwResType) { case restype::ASCIIPATH: case restype::ASCIISTRING: value += StringToWString ((CHAR *)bytes.data ()); break; case restype::UTF8PATH: case restype::UTF8STRING: value += StringToWString ((CHAR *)bytes.data (), CP_UTF8); break; case restype::STRING: case restype::PATH: value += (WCHAR *)bytes.data (); break; case restype::EMBEDDEDDATA: value += L"<" + std::to_wstring (ret) + L" bytes>"; break; } } auto qualifierSet = decisionInfoSection.vec_qua_set [candidate.wQualifierSet]; std::wstring qualifiers; for (auto qual : qualifierSet.vecQuals) { std::wstring str = L" "; str += EnumToStringW (qual.eType) + L" = " + qual.swValue + L"\n"; qualifiers += str; } out << L" Value {" << value << L"}" << std::endl; out << qualifiers; double progress = (double)((cnt_i + (cnt_j + (double)cnt_k / candidateSet.vecCandidates.size ()) / resmap_sect.mapCandidateSet.size ()) / pri_desp.vec_ref_rm.size () * 100.0); std::wcout << L"\r " << loadchar.nowchar << L" " << std::fixed << std::setprecision (2) << progress << L" % [(" << cnt_k << L" / " << candidateSet.vecCandidates.size () << L") of (" << cnt_j << L" / " << resmap_sect.mapCandidateSet.size () << L") of (" << cnt_i << L" / " << pri_desp.vec_ref_rm.size () << L")]" << L" " ; cnt_k ++; } cnt_j ++; } int i = 0; cnt_i ++; } int j = 0; std::wcout << L"\r 100 % " << std::endl; out << L"Read Completed: "; SYSTEMTIME st_end; GetLocalTime (&st_end); ZeroMemory (buf, 60 * sizeof (WCHAR)); swprintf (buf, 64, L"%4d.%02d.%02d %02d:%02d:%02d", st_end.wYear, st_end.wMonth, st_end.wDay, st_end.wHour, st_end.wMinute, st_end.wSecond); out << buf << std::endl; out << L"Time Spend: " << DiffSystemTimeMs (st_start, st_end) * 0.001 << L"s" << std::endl; #endif } }; bool pri::sect_resmap::parse () { reset (); istreamstream fp (sect.childst.ifile); sect.childst.seek (); ulint sectpos = 0; fp->Seek (lint (0), STREAM_SEEK_CUR, sectpos.ptr_union ()); fp->Read (&content, sizeof (content), nullptr); bool res = (!ver2) ? (content.wEnvRefLength != 0 && content.wRefCount != 0) : (content.wEnvRefLength == 0 && content.wRefCount == 0); if (!res) return false; { auto currpos = fp.position (); try { auto dest = sect.pri_file.get_section_by_ref (ref_sect (content.wDecInfSectIndex)); dest.parse (); } catch (const std::exception &e) { parseError = true; return false; } fp.seek (currpos, istreamstream::seekpos::start); } bvecEnvRefData.resize (content.wEnvRefLength); bvecScheRefData.resize (content.wHSRefLength); ZeroMemory (bvecEnvRefData.data (), sizeof (BYTE) * content.wEnvRefLength); ZeroMemory (bvecScheRefData.data (), sizeof (BYTE) * content.wHSRefLength); fp->Read (bvecEnvRefData.data (), sizeof (BYTE) * content.wEnvRefLength, nullptr); fp->Read (bvecScheRefData.data (), sizeof (BYTE) * content.wHSRefLength, nullptr); if (content.wHSRefLength != 0) { bytesstream srdata (bvecScheRefData); srdata.read (&hschema_ref.verHschema, sizeof (hschema_ref.verHschema)); if (hschema_ref.verHschema.dwUnknown1 != 0) return false; srdata.read (&hschema_ref.part2, sizeof (hschema_ref.part2)); if (hschema_ref.part2.wUnknown1 != 0) return false; hschema_ref.swUniqueId = ReadStringEndwithNullW (fp); } for (size_t i = 0; i < content.wResTypeEntCount; i ++) { RES_VALUE_TYPE_TABLE rvtt; fp->Read (&rvtt, sizeof (rvtt), nullptr); if (rvtt.dwUnknown1 != 4) return false; vecResTypes.push_back ((RES_VALUE_TYPE)rvtt.dwResType); } for (size_t i = 0; i < content.wItemEntCount; i ++) { ITEM_ITEMINFO_GROUP_TABLE_ENTRY iigte; fp->Read (&iigte, sizeof (iigte), nullptr); vecItemToItemInfoGroup.push_back (iigte); } for (size_t i = 0; i < content.wItemGroupEntCount; i ++) { ITEMINFO_GROUP_TABLE_ENTRY iigte; fp->Read (&iigte, sizeof (iigte), nullptr); vecItemInfoGroups.push_back (iigte); } for (size_t i = 0; i < content.dwItemTableEntCount; i ++) { ITEM_ITEMINFO_TABLE_ENTRY iite; fp->Read (&iite, sizeof (iite), nullptr); vecItemInfo.push_back (iite); } std::vector largeTable (content.dwTableExtCount); fp->Read (largeTable.data (), sizeof (BYTE) * content.dwTableExtCount, nullptr); if (largeTable.size () != 0) { bytesstream bytes (largeTable); TABLE_EXT_BLOCK teb; bytes.read (&teb, sizeof (teb)); for (size_t i = 0; i < teb.dwItemAdditEntCount; i ++) { ITEM_ITEMINFO_GROUP_TABLE_ENTRY iiigte; bytes.read (&iiigte, sizeof (iiigte)); vecItemToItemInfoGroup.push_back (iiigte); } for (size_t i = 0; i < teb.dwItemGroupAdditEntCount; i ++) { ITEMINFO_GROUP_TABLE_ENTRY iigte; bytes.read (&iigte, sizeof (iigte)); vecItemInfoGroups.push_back (iigte); } for (size_t i = 0; i < teb.dwItemTableAdditEntCount; i ++) { ITEM_ITEMINFO_TABLE_ENTRY iiite; bytes.read (&iiite, sizeof (iiite)); vecItemInfo.push_back (iiite); } if (bytes.position () > bytes.length ()) throw std::exception ("Error: invalid data in ResourceMap or ResourceMap2 Section."); } for (size_t i = 0; i < content.dwCandidateCount; i ++) { BYTE bType = -1; fp->Read (&bType, sizeof (bType), nullptr); switch (bType) { case 0x00: { CANDIDATE_TYPE rvtype = 0; fp->Read (&rvtype, sizeof (rvtype), nullptr); CANDIDATE0_DATA cdata; cdata.bResValueType = (BYTE)vecResTypes.at (rvtype); auto &length = cdata.wEmbeddedLength; auto &stringOffset = cdata.dwEmbeddedOffset; fp >> length >> stringOffset; vecCandidateInfo.emplace_back (CANDIDATE_INFO (cdata)); } break; case 0x01: { CANDIDATE_TYPE rvtype = 0; fp->Read (&rvtype, sizeof (rvtype), nullptr); CANDIDATE1_DATA cdata; auto &resourceValueType = cdata.bResValueType; auto &sourceFileIndex = cdata.wSrcFile; auto &valueLocation = cdata.wDataIndex; auto &dataItemSection = cdata.wSectIndex; resourceValueType = (BYTE)vecResTypes.at (rvtype); fp >> sourceFileIndex >> valueLocation >> dataItemSection; vecCandidateInfo.emplace_back (CANDIDATE_INFO (cdata)); } break; default: { throw std::domain_error ("Error: invalid data read in ResourceMap or ResourceMap2 section."); } break; } } ulint strbegpos = 0; fp->Seek (lint (0), STREAM_SEEK_CUR, strbegpos.ptr_union ()); for (size_t i = 0; i < vecItemToItemInfoGroup.size (); i ++) { auto &itemToItemGroup = vecItemToItemInfoGroup [i]; ITEMINFO_GROUP_TABLE_ENTRY itemInfoGroup; if (itemToItemGroup.wItemInfoGroupIndex < vecItemInfoGroups.size ()) itemInfoGroup = (vecItemInfoGroups [itemToItemGroup.wItemInfoGroupIndex]); else itemInfoGroup = {1, (WORD)(itemToItemGroup.wItemInfoGroupIndex - vecItemInfoGroups.size ())}; for (size_t j = itemInfoGroup.wFirstItemIndex; j < itemInfoGroup.wFirstItemIndex + itemInfoGroup.wItemInfoCount; j ++) { auto &itemInfo = vecItemInfo [j]; auto decIndex = itemInfo.wDecisionIndex; auto &decSect = sect.pri_file.get_section_by_ref (ref_sect (content.wDecInfSectIndex)); decSect.parse (); auto dec = decSect.vec_dec [decIndex]; CANDIDATE_SET candidateSet; std::vector &candidates = candidateSet.vecCandidates; for (size_t k = 0; k < dec.verQualSets.size (); k ++) { auto can_info = vecCandidateInfo [itemInfo.wFirstCandiIndex + k]; switch (can_info.bCandidateType) { case 0x01: { REF_FILE_REF sourceFile; if (!can_info.objCandidate._1.wSrcFile) sourceFile.setnull (); else sourceFile.set (can_info.objCandidate._1.wSrcFile - 1); candidates.push_back ( CANDIDATE ( dec.verQualSets [k].wIndex, (RES_VALUE_TYPE)can_info.objCandidate._1.bResValueType, sourceFile, DATA_ITEM_REF (can_info.objCandidate._1.wDataIndex, can_info.objCandidate._1.wSectIndex ) ) ); } break; case 0x00: { BYTE_SPAN bspan ( sectpos + strbegpos + ulint (can_info.objCandidate._0.dwEmbeddedOffset), can_info.objCandidate._0.wEmbeddedLength ); candidates.push_back ( CANDIDATE ( dec.verQualSets [k].wIndex, (RES_VALUE_TYPE)can_info.objCandidate._1.bResValueType, bspan ) ); } break; } } WORD resourceMapItemIndex = itemToItemGroup.wFirstIndexProperty + (j - itemInfoGroup.wFirstItemIndex); candidateSet.refResMapItem = RES_MAP_ITEM_REF (content.wHSSectIndex, resourceMapItemIndex); candidateSet.wDecisionIndex = decIndex; mapCandidateSet [resourceMapItemIndex] = candidateSet; } } return valid (); } #ifdef UNALIGN_MEMORY #pragma pack(pop) #endif