åã®èšäºã§çŽæããããã«ããªããŒã¹ãšã³ãžãã¢ãªã³ã°ãšé·æéã®ãã¬ã€ã³ã¹ããŒãã³ã°ã«ãã£ãŠã¢ã³ãã¹ããŒã ã³ãã¥ããã£ãéãããšãã§ããSteamã€ã³ãã©ã¹ãã©ã¯ãã£ã®äžéšã«ã€ããŠã®èšäºãå
¬éãå§ããŠããŸãã
æè¿ãŸã§ãGCFãã¡ã€ã«ã¯VALVEããªãªãŒã¹ãããã¹ãŠã®ã²ãŒã ã®æšæºã§ãããä»ã®ãã¹ãŠã®ã²ãŒã ã¯NCFã§ããã ãããã®ãã¡ã€ã«èªäœã¯ãããã€ãã®ä¿è·ã¬ãã«ãæã€ãã¡ã€ã«ã·ã¹ãã ã€ã¡ãŒãžãè¡šããŸãã NCFãšGCFã®éãã¯ãåè
ã«ã¯ããããŒã®ã¿ãå«ãŸãããããã«å±ãããã¡ã€ã«ã¯å¥ã®ãã£ã¬ã¯ããªïŒ
<Steamãã£ã¬ã¯ããª> / SteamApps / common / <ã²ãŒã å> ïŒã«ããããšã§ãã ãããã£ãŠãGCFã«ã€ããŠèª¬æããåŸã§NCFã®ãã¹ãŠã®æ©èœã説æããŸãã
ãã®èšäºã§ã¯ããããã®ãã¡ã€ã«ã®æ§é ã詳现ã«åæããã©ã€ãã©ãªã®äŸã䜿çšããŠäœæ¥ããŸãïŒãªã³ã¯ã¯èšäºã®æåŸã«ãããŸãïŒã æåã¯éåžžã«éå±ã§ã-æ§é ã®èª¬æãšãããã®ãã£ãŒã«ãã®ç®çã æããããããããã®ã¯åœŒãã®åŸã«ãªããŸã...
ããã®ãã¹ãŠã®ã³ãŒãã¯ãSteamã©ã€ãã©ãªã®ãªããŒã¹ãšã³ãžãã¢ãªã³ã°ã®çµæã§ãã ãã¡ã€ã«åœ¢åŒã«é¢ããæ
å ±ã®ã»ãšãã©ã¯
ãªãŒãã³ãœãŒã¹ããååŸãããŸããããå°ãè£è¶³ãããã£ãã·ã¥ãã¡ã€ã«ã§ã®äœæ¥ã倧å¹
ã«æé©åããŸããïŒåœææã人æ°ã®ãã£ãHLLIBã©ã€ãã©ãªãšæ¯èŒããŠãïŒã
äžè¬çãªãã¡ã€ã«æ§é
ãã¡ã€ã«ã¯ãããããŒãšã³ã³ãã³ãèªäœã®2ã€ã®éšåã«è«ççã«åå²ãããŸãã ã³ã³ãã³ãã¯ãããã¯ã«åå²ãããåãããã¯ã¯8kBã®ã»ã¯ã¿ãŒã«åå²ãããŸãããããã®ã»ã¯ã¿ãŒã¯ç¹å®ã®ãã¡ã€ã«ã«å±ãããã®ã·ãŒã±ã³ã¹ã¯ããããŒã«èšè¿°ãããŠããŸãã ãã¹ãŠã®ããããŒã«ã¯ã4ãã€ãæŽæ°ã®ãã£ãŒã«ããå«ãŸããŸãïŒäŸå€ã¯ããã¡ã€ã«åãšãã£ã¬ã¯ããªåã®ãªã¹ããæ
åœããéšåã§ãïŒã
ããããŒã¯æ¬¡ã®æ§é ã§æ§æãããŸãã
- ãã¡ã€ã«ããããŒ
- BlockAllocationTableHeader
- BlockAllocationTable []
- FileAllocationTableHeader
- FileAllocationTable []
- ãããã§ã¹ãããããŒ
- ãããã§ã¹ã[]
- ãã¡ã€ã«å
- HashTableKeys []
- HashTableIndices []
- MinimumFootprints []
- UserConfig []
- ManifestMapHeader
- ManifestMap []
- ãã§ãã¯ãµã ããŒã¿ã³ã³ãã
- FileIdChecksumTableHeader
- FileIdChecksums []
- ãã§ãã¯ãµã []
- ãã§ãã¯ãµã 眲å
- LatestApplicationVersion
- ããŒã¿ããããŒ
æåã«ç®ãåŒãã®ã¯
ChecksumSignatureã§ããããã¯ããã¡ã€ã«ã®ãã§ãã¯ãµã ãåŠçããããããŒã®äžéšã®æå·åãããããã·ã¥ã§ãã
ããããã¹ãŠã®ããããŒãšãããã®ãã£ãŒã«ãã®ç®çã«ã€ããŠã¯ãåŸã§èª¬æããŸãã
泚ææ·±ãèªãã§ããªã人ã®ããã«ãç¹ã«æèšããªãéããã»ãšãã©ãã¹ãŠã®ããããŒã®ãã¹ãŠã®ãã£ãŒã«ãã¯4ãã€ãæŽæ°ïŒC ++ã§ã¯
uint32_t ïŒã§ããããšã
æãåºããŸãã
ãã¡ã€ã«ããããŒ
ååã«åºã¥ããŠããã¡ã€ã«å
šäœã®ããããŒã§ããã次ã®ãã£ãŒã«ããå«ãŸããŠããŸãã
- HeaderVersion
- ãã£ãã·ã¥ã¿ã€ã
- FormatVersion
- ApplicationID
- ApplicationVersion
- æèŒãããŠããŸã
- ãããŒ0
- FileSize
- ClusterSize
- ã¯ã©ã¹ã¿ãŒæ°
- ãã§ãã¯ãµã
HeaderVersion-ãã®ããããŒã®ããŒãžã§ã³ã瀺ããåžžã«0x00000001ã
CacheType -GCFã®å Žåã¯0x00000001ãNCFã®å Žåã¯0x00000002ã
FormatVersion-æ®ãã®ããããŒã®æ§é ããŒãžã§ã³ã瀺ããŸãã ææ°ããŒãžã§ã³ã¯6ã§ããããã«ã€ããŠã¯åŸã§èª¬æããŸãã
ApplicationID-ãã¡ã€ã«èå¥åïŒAppIDïŒã
ApplicationVersionã¯ããã¡ã€ã«ã®ã³ã³ãã³ãã®ããŒãžã§ã³ã§ãã æŽæ°ã®å¿
èŠæ§ãå¶åŸ¡ããŸãã
IsMounted-ãã¡ã€ã«ãçŸåšå¥ã®ã¢ããªã±ãŒã·ã§ã³ã«ãã£ãŠããŠã³ããããŠããå Žåã0x00000001ãå«ãŸããŸãã çŸåšã¯äœ¿çšãããŠããªããããåžžã«0x00000000ã§ãã
Dummy0ã¯ã0x00000000ãå«ãã¢ã©ã€ã¡ã³ããã£ãŒã«ãã§ãã
FileSizeã¯ãã¡ã€ã«ã®åèšãµã€ãºã§ãã 4GBãè¶
ããå Žåããã®ãã£ãŒã«ãã«ã¯å·®
<file size> -ffffffffãå«ãŸãããã¡ã€ã«ãµã€ãºèªäœã¯ä»¥äžã«åºã¥ããŠèšç®ãããŸã
ããŒã¿ãããã¯ã®ãµã€ãºãšæ°éã
ClusterSize-ã³ã³ãã³ãå
ã®ããŒã¿ãããã¯ã®ãµã€ãºã GCFã®å Žåã0x00002000ãå«ãŸããNCFã®å Žåã0x00000000ãå«ãŸããŸãã
ClusterCount-ã³ã³ãã³ãå
ã®ããŒã¿ãããã¯ã®æ°ã
ãã§ãã¯ãµã ã¯ãããããŒã®ãã§ãã¯ãµã ã§ãã 次ã®é¢æ°ã«ãã£ãŠèšç®ãããŸãã
UINT32 HeaderChecksum(UINT8 *lpData, int Size) { UINT32 Checksum = 0; for (int i=0 ; i<Size ; i++) Checksum += *(lpData++); return Checksum; }
æåã®ãã©ã¡ãŒã¿ãŒã¯ãã€ã³ã¿ãŒãæ§é äœã«æž¡ãã2çªç®ã®ãã©ã¡ãŒã¿ãŒã¯ãµã€ãºã
ãã§ãã¯ãµã ãã£ãŒã«ããé€ããŠïŒã€ãŸãã4æªæºïŒæž¡ããŸãã
BlockAllocationTableHeader
ãããã¯ããŒãã«ã®èª¬æãå«ãŸããŠããŸãïŒã»ã¯ã¿ãŒã§ã¯ãããŸããïŒïŒïŒ
- ãããã¯æ°
- 䜿çšãããã¯
- LastUsedBlock
- ãããŒ0
- ãããŒ1
- ãããŒ2
- ãããŒ3
- ãã§ãã¯ãµã
BlockCount-ãã¡ã€ã«å
ã®ãããã¯ã®ç·æ°ãå«ãŸããŠããŸãã
BlocksUsed-䜿çšãããŠãããããã¯ã®æ°ã åžžã«ãããã¯ã®ç·æ°ãããå°ãªããªããŸãã ããã«è¿ã¥ããšãåèšæ°éã®å€ãå¢å ããåŸç¶ã®ãã¹ãŠã®ããããŒãåæ§ç¯ãããæåã®ããŒã¿ã»ã¯ã¿ãŒããã¡ã€ã«ã®æåŸã«ç§»åããŠãããããŒçšã®ã¹ããŒã¹ã解æŸãããŸãã
LastUsedBlock-æåŸã«äœ¿çšããããããã¯ã®ã€ã³ããã¯ã¹ã
Dummy0ãDummy1ãDummy2ãDummy2-ã¢ã©ã€ã¡ã³ããã£ãŒã«ãã«ã¯0x00000000ãå«ãŸããŸãã
ãã§ãã¯ãµã ã¯ãããããŒã®ãã§ãã¯ãµã ã§ãã 以åã®ãã¹ãŠã®ãã£ãŒã«ãã®åèšãå«ãŸããŸãã
BlockAllocationTable
ããã¯
BlockAllocationTableEntryæ§é äœã®é
åã§
ãã ããã®æ°ã¯ãããã¯ã®ç·æ°ïŒ
BlockAllocationTableHeader.BlockCount ïŒãšåãã§ãã
- uint16_tãã©ã°
- uint16_t Dummy0
- FileDataOffset
- FiledataSize
- Firstclusterindex
- NextBlockIndex
- PreviousBlockIndex
- ãããã§ã¹ãã€ã³ããã¯ã¹
ãã©ã° -ãããã¯ã®ããããã©ã°ãå«ãŸããŸãã å¯èœãªãã¹ã¯ïŒ
- 0x8000-ãããã¯ã¯äœ¿çšäžã§ãã
- 0x4000-ãã¡ã€ã«ã®ããŒã«ã«ã³ããŒãåªå
ãããŸãã
- 0x0004-ãããã¯ã¯æå·åãããŠããŸãã
- 0x0002-ãããã¯ã¯æå·åããã³å§çž®ãããŸãã
- 0x0001-ãããã¯ã«ã¯çããŒã¿ïŒRAWïŒãå«ãŸããŸãã
Dummy0ã¢ã©ã€ã¡ã³ããã£ãŒã«ãã0x0000ãå«ãŸããŠããŸãã
FileDataOffsetã«ã¯ããã®ãããã¯ãå±ãããã¡ã€ã«ã«å¯Ÿãããã®ãããã¯ã®ãªãã»ãããå«ãŸããŸãã
FileDataSize-ãã®ãããã¯ã«ä¿åãããŠãããã¡ã€ã«ã®ãã©ã°ã¡ã³ãã®ãµã€ãºã
FirstClusterIndex-ã¯ã©ã¹ã¿ãŒããŒãã«å
ã®æåã®ã¯ã©ã¹ã¿ãŒã®ã€ã³ããã¯ã¹ã
NextBlockIndex-次ã®ãããã¯ã®ã€ã³ããã¯ã¹ã
BlockAllocationTableHeaderã®å€ãå«ãŸããŸã
ã ããããã®ãã¡ã€ã«ã®ãã§ãŒã³å
ã®æåŸã®ãããã¯ã§ããå Žåã¯ã
BlockCount ã
PreviousBlockIndex-ãã§ãŒã³å
ã®åã®ãããã¯ã®ã€ã³ããã¯ã¹ãå«ãŸããŸãã æåã®å Žåã¯ãå€
BlockAllocationTableHeaderãå«ãŸããŸã
ã ãããã¯æ°ManifestIndex-ãã®ãããã¯ã®ãããã§ã¹ãã€ã³ããã¯ã¹ã
ããŒãã«ã®ã€ã³ããã¯ã¹ã¯ã
ManifestMapãªã¹ãã®ãããã¯çªå·ã§ãã
FileAllocationTableHeader
ã»ã¯ã¿ãŒããŒãã«ããããŒïŒ
- ã¯ã©ã¹ã¿ãŒæ°
- FirstUnusedEntry
- IsLongTerminator
- ãã§ãã¯ãµã
ClusterCount-ã»ã¯ã¿ãŒã®æ°ãå«ãŸããŠããŸãã
FileHeader.ClusterCountãšçããå€ãå«ãŸããŸãã
FirstUnusedEntry-æåã®æªäœ¿çšã»ã¯ã¿ãŒã®ã€ã³ããã¯ã¹ã
IsLongTerminator-ã»ã¯ã¿ãŒãã§ãŒã³ã®çµããã瀺ãå€ãå®çŸ©ããŸãã 0x00000000ãå«ãŸããŠããå Žåãã¿ãŒãããŒã¿ãŒã¯0x0000FFFFã§ããããã以å€ã®å Žåã¯-0xFFFFFFFFã§ãã
ãã§ãã¯ãµã ã¯ãããããŒã®ãã§ãã¯ãµã ã§ãã
BlockAllocationTableHeaderãšåæ§ã«ãåã®ããããŒãã£ãŒã«ãã®åèšã§ãã
FileAllocationTable
ã¿ã€ã
uint32_tã® FileAllocationTableHeader.ClusterCountãšã³ããªãå«ãã»ã¯ã¿ãŒããŒãã«ã åã»ã«ã«ã¯ããã§ãŒã³å
ã®æ¬¡ã®ã¯ã©ã¹ã¿ãŒã®ã€ã³ããã¯ã¹ãŸãã¯ã¿ãŒãããŒã¿ãŒå€ãå«ãŸããŸãïŒãã§ãŒã³ã®æåŸã®å Žåã¯ã
FileAllocationTableHeader宣èšãåç
§ããŠãã ããïŒã
ãªã¹ãã®ã€ã³ããã¯ã¹ã¯ã»ã¯ã¿ãŒçªå·ã§ãã
ãããã§ã¹ãããããŒ
ãããã§ã¹ãããŒãã«ã®èª¬æãå«ãŸããŸãã
- HeaderVersion
- ApplicationID
- ApplicationVersion
- Nodecount
- ãã¡ã€ã«æ°
- CompressionBlockSize
- BinarySize
- ååãµã€ãº
- HashTableKeyCount
- NumOfMinimumFootprintFiles
- NumOfUserConfigFiles
- ããããã¹ã¯
- æçŽ
- ãã§ãã¯ãµã
HeaderVersionã¯ããããŒã®ããŒãžã§ã³ã§ãã 0x00000004ãå«ãŸããŸãã
ApplicationID-ãã¡ã€ã«èå¥åã
FileHeader.ApplicationIDãšåãã§ãã
ApplicationVersionã¯ããã¡ã€ã«ã®ã³ã³ãã³ãã®ããŒãžã§ã³ã§ãã
FileHeader.ApplicationVersionãšåãã§ãã
NodeCount-ãããã§ã¹ãèŠçŽ ã®æ°ã
FileCount-ãããã§ã¹ãã§å®£èšãããïŒããã³ãã£ãã·ã¥ã«å«ãŸããïŒãã¡ã€ã«ã®æ°ã
CompressionBlockSize-å§çž®ãããã¯ïŒéå§çž®ããŒã¿ïŒã®æ倧ãµã€ãºã
BinarySize-ãããã§ã¹ããµã€ãºïŒãã®æ§é ãå«ãïŒã
NameSize-èŠçŽ åãå«ãããŒã¿ãããã¯ã®ãµã€ãºïŒãã€ãåäœïŒã
HashTableKeyCount-ããã·ã¥ããŒãã«ã®å€ã®æ°ã
NumOfMinimumFootprintFiles-ã¢ããªã±ãŒã·ã§ã³ã®å®è¡ã«æäœéå¿
èŠãªãã¡ã€ã«ã®æ°ïŒããã¯ãã£ã¹ã¯ã«è§£åããå¿
èŠããããŸãïŒã
NumOfUserConfigFiles-ãŠãŒã¶ãŒæ§æãã¡ã€ã«ã®æ°ã ãã®ãã¡ã€ã«ããã£ã¹ã¯äžã«ããå Žåãã²ãŒã ã®éå§æã«äžæžãããããåªå
床ãé«ããªããŸãã
ããããã¹ã¯ -
ããããã¹ã¯ãå«ãŸããŸãã ãã¡ã€ã«ã®ãããªãã¯ããŒãžã§ã³ã§ã¯ãåžžã«0x00000000ãå«ãŸããŠããŸãã
æçŽã¯ããããã§ã¹ããæŽæ°ããããã³ã«ã©ã³ãã ã«çæãããäžæã®çªå·ã§ãã
ãã§ãã¯ãµã -ãã§ãã¯ãµã ã Adler32ã¢ã«ãŽãªãºã ã䜿çšããŠèšç®ãããŸãã èšç®ã¢ã«ãŽãªãºã ã¯ãããããŒã®èª¬æã®åŸã«èšèŒãããŸãã
ãããã§ã¹ã
ãã£ãã·ã¥å
ã®ãã¹ãŠã®ãã¡ã€ã«ã®èª¬æãå«ãããªãŒã ããŒãã«ã®ãµã€ãºã¯
ManifestHeader.NodeCountã®å€ãšåãã§ãã ããŒãã«ã®ãã¹ãŠã®èŠçŽ ã¯ã次ã®æ§é ã§è¡šãããŸãã
- Nameoffff
- CountOrSize
- Fileid
- å±æ§
- Parentindex
- NextIndex
- ãã£ã€ã«ãã€ã³ããã¯ã¹
NameOffset-察å¿ããããŒã¿ãããã¯å
ã®èŠçŽ ã®ååã®ãªãã»ããã
CountOrSize-èŠçŽ ã®ãµã€ãºã ãã£ã¬ã¯ããªã®å Žåã¯ãåã®æ°ã«çããããã¡ã€ã«ã®å Žåã¯ããã¡ã€ã«ïŒãŸãã¯ãã®ãããã§ã¹ãã§èšè¿°ããããã¡ã€ã«ã®äžéšïŒã®ãµã€ãºã«çŽæ¥çãããªããŸãã
FileId-ãã¡ã€ã«èå¥åã 倧ããªãã¡ã€ã«ã®è€æ°ã®ãããã§ã¹ãããªã³ã¯ãããã§ãã¯ãµã ãªã¹ããæ€çŽ¢ããŸãã
å±æ§ -ãã¡ã€ã«å±æ§ããããã£ãŒã«ãã å¯èœãªå€ïŒç¢ºèªæžã¿ããïŒïŒ
- 0x00004000-ããŒãã¯ãã¡ã€ã«ã§ãã
- 0x00000100-æå·åããããã¡ã€ã«ã
- 0x00000001-æ§æãã¡ã€ã«ã ããŒã«ã«ã³ããŒã¯äžæžããããŸããã
ParentIndexã¯ã芪èŠçŽ ã®ã€ã³ããã¯ã¹ã§ãã ã«ãŒãèŠçŽ ã®å Žåã¯0xFFFFFFFFã§ãã
NextIndex-çŸåšã®ããªãŒã¬ãã«ã§ã®æ¬¡ã®èŠçŽ ã®ã€ã³ããã¯ã¹ã
ChildIndexã¯ãæåã®åã®ã€ã³ããã¯ã¹ã§ãã
NextIndexããã³
ChildIndexã®èŠçŽ ããªãå Žåãå€0x00000000ãå«ãŸããŸãã
ããªãŒã«ã¯å°ãªããšã1ã€ã®èŠçŽ ãã€ãŸãã«ãŒããå«ãŸããŠããå¿
èŠããããŸãã
ããªãŒé
ç®ãå«ããªã¹ãã®ã€ã³ããã¯ã¹ã¯é
ç®çªå·ã§ãïŒåŸã§äœ¿çšãããŸãïŒ
ãã¡ã€ã«å
ãµã€ãº
ManifestHeader.NameSizeãã€ãã®
charããŒã¿ãããã¯ã ãããã§ã¹ãããªãŒã«èšè¿°ãããŠããèŠçŽ ã®ååã§ãã
ãã«çµäºæååãå«ãŸããŠã
ãŸã ã å¿
é ã¯ãæåã®ã«ãŒãèŠçŽ ïŒç©ºã®æååïŒã®ååšã§ãã èŠçŽ åã®ãªãã»ããã¯ãå€
Manifest []ã§æå®ãããŸã
ãHashTableKeys
èŠçŽ åã®ããã·ã¥ããŒãã«ãå«ãŸããŸãã
å°æåã®æååã®
Jenkinsããã·ã¥é¢æ° lookup2ãã掟çã
ãã€ã³ããã¯ã¹ã«åæ£ããã
HashTableIndicesã®ã€ã³ããã¯ã¹å€ãå«ãŸããŸãã 詳现ã«ã€ããŠã¯ãèŠçŽ ã®æ€çŽ¢ã®èª¬æã§èª¬æããŸãã
HashTableIndices
åã®ããŒãã«ã®å€ã«ãã£ãŠåç
§ãããèŠçŽ ã®ã€ã³ããã¯ã¹ã®ããŒãã«ãå«ãŸããŸãã èŠçŽ ã®æ°ã¯
ManifestHeader.NodeCountã§ãã
MinimumFootprints
ã¢ããªã±ãŒã·ã§ã³ã®èµ·åæã«å±éããå¿
èŠããã
ãããã§ã¹ãã®ã¢ã€ãã çªå·ã®ãªã¹ããå«ãŸããŠããŸãã
Userconfigs
ãŠãŒã¶ãŒæ§æãã¡ã€ã«ã§ãã
Manifestã®ã¢ã€ãã çªå·ããªã¹ãããŸãã
ManifestMapHeader
ãããã§ã¹ããããããããŒïŒ
HeaderVersionã¯ããããŒã®ããŒãžã§ã³ã§ãã 0x00000001ãšçããã
Dummy0ã¯ã¢ã©ã€ã¡ã³ãå€ã§ãã 0x00000000ãå«ãŸããŸãã
ãããã§ã¹ãããã
åèŠçŽ ã®æåã®ãããã¯ïŒ
BlockAllocationTableæ§é äœïŒãžã®ãªã³ã¯ã®ããŒãã«ãå«ãŸããŸãã èŠçŽ ã€ã³ããã¯ã¹ã¯ããããã§ã¹ãããªãŒå
ã®èŠçŽ ã®çªå·ã§ãã ãã£ãã·ã¥ã«ä¿åãããŠããªããã£ã¬ã¯ããªããã³ãã¡ã€ã«ïŒãµã€ãºããŒããŸãã¯NCFã®å ŽåïŒã«ã¯ã
BlockAllocationTableHeader.BlockCountãšçããå€ãå«ãŸããŸãã
ãã§ãã¯ãµã ããŒã¿ã³ã³ãã
ãã§ãã¯ãµã ãæ ŒçŽããã³ã³ããã®ããããŒïŒ
- HeaderVersion
- ãã§ãã¯ãµã ãµã€ãº
HeaderVersionã¯ããããŒã®ããŒãžã§ã³ã§ãã 0x00000001ãšçããã
ChecksumSize-ã³ã³ããã®ãµã€ãºã
LatestApplicationVersionãå«ã次ã®æ§é ããèšç®ãããŸãã
FileIdChecksumTableHeader
ãã§ãã¯ãµã ã€ã³ããã¯ã¹ããŒãã«ã®ã¿ã€ãã«ïŒ
- ãã©ãŒãããã³ãŒã
- ãããŒ0
- Fileidcount
- ãã§ãã¯ãµã ã«ãŠã³ã
FormatCodeã¯å®æ°ã§ãã 0x14893721ãšçããã
Dummy0ã¯ã¬ããªã³ã°ãã£ãŒã«ãã§ãã å€0x00000001ãå«ãŸããŸãã
FileIdCount -
element-first-hashããŒãã«å
ã®
èŠçŽ ã®æ°ã
ChecksumCount-ãã§ãã¯ãµã ãªã¹ãå
ã®èŠçŽ ã®æ°ã
FileIdChecksums
ãã¡ã€ã«ããã§ãã¯ãµã ãªã¹ãã«ãªã³ã¯ããããŒãã«ïŒ
- ãã§ãã¯ãµã ã«ãŠã³ã
- Firstchecksumindex
ChecksumCount-ãã®èŠçŽ ã®ãªã¹ãå
ã®ãã§ãã¯ãµã ã®æ°ã
FirstChecksumIndex-ãªã¹ãå
ã®æåã®ãã§ãã¯ãµã ã®ã€ã³ããã¯ã¹ã
ã€ã³ããã¯ã¹ã¯ãå€
Manifest []ãFileIdã§ãã
ãã§ãã¯ãµã
ãã§ãã¯ãµã ã®ãªã¹ãã é£ç¶ãããµããªã¹ããå«ãŸãããã®æåã®èŠçŽ ã¯å€
FileIdChecksums []ãFirstChecksumIndexã«ãã£ãŠåç
§ãããŸãã
å€ã¯ã次ã®ã¢ã«ãŽãªãºã ã䜿çšããŠèšç®ãããŸãã
UINT32 Checksum(UINT8 *lpData, UINT32 uiSize) { return (adler32(0, lpData, uiSize) ^ crc32(0, lpData, uiSize)); }
ãã§ãã¯ãµã 眲å
ãã§ãã¯ãµã ãããã¯ã®çœ²åã SHA-1ã¢ã«ãŽãªãºã ã«ãã£ãŠèšç®ããã
RSASSA-PKCS1-v1_5ã¢ã«ãŽãªãºã ã«ãã£ãŠæå·åããããã§ãã¯ãµã ãããã¯ã®ããã·ã¥å€ãå«ãŸããŸãã
LatestApplicationVersion
ãã®ãã£ãŒã«ãã«ã¯ããã§ãã¯ãµã ãããã¯ã®ããŒãžã§ã³ãå«ãŸããŸãã åã³ã³ãã³ãã®æŽæ°åŸã«ææ°ã«æŽæ°ãããŸãã
ããŒã¿ããããŒ
ãã£ãã·ã¥å
ã®ããŒã¿ã®ç©ççãªé
眮ã説æããã¿ã€ãã«ïŒ
- ã¯ã©ã¹ã¿ãŒæ°
- ClusterSize
- æåã®ã¯ã©ã¹ã¿ãŒãªãã»ãã
- 䜿çšã¯ã©ã¹ã¿ãŒ
- ãã§ãã¯ãµã
ClusterCount-ã»ã¯ã¿ãŒã®æ°ã å€ã¯
FileHeader.ClusterCountãã£ãŒã«ããšåãã§ãã
ClusterSize-ã»ã¯ã¿ãŒãµã€ãºã å€ã¯
FileHeader.ClusterSizeãã£ãŒã«ããšåãã§ãã
FirstClusterOffset-ãã¡ã€ã«ã®å
é ã«å¯Ÿããæåã®ã»ã¯ã¿ãŒã®ãªãã»ããã
ClustersUsed-䜿çšãããŠããã»ã¯ã¿ãŒã®æ°ã
ãã§ãã¯ãµã ã¯ãããããŒã®ãã§ãã¯ãµã ã§ãã å
è¡ããããããŒãã£ãŒã«ãã®åèšã«çããã
ã³ã³ãã³ããæŽæ°ãããšã䜿çšãããã»ã¯ã¿ãŒã®æ°ãæžå°ããå¯èœæ§ããããŸãã ãã®ãããªå Žåã解æŸãããã»ã¯ã¿ãŒã¯ãã¡ã€ã«ã®æåŸã«è»¢éãããå°æ¥ã®æŽæ°çšã«ã¹ããŒã¹ã確ä¿ãããŸãã
ã¢ã«ãŽãªãºã
æåŸã«ãæãèå³æ·±ããã®ãç»å ŽããŸãã-ãããã®æ§é ã§åäœããã³ãŒãã®æãèå³æ·±ãäŸãã詳现ãªèª¬æãšãšãã«ãããŸãã å®å
šãªãœãŒã¹ããã±ãŒãžã¯ç§ã®
ãªããžããªã«ãããŸã ã
ãã¡ã€ã«ãµã€ãºã®èšç®
ã»ãšãã©ã®å Žåããã¡ã€ã«ãµã€ãºã¯
Manifest []ãCountOrSizeãã£ãŒã«ãã®å€ãšåãã§ãã ãã ãã4GBãè¶
ãããã¡ã€ã«ã®å Žåããã®æ¹æ³ã¯é©ããŠããŸããã VALVEããã°ã©ããŒã¯æ¬¡ã®æ¹æ³ã§ãããåé¿ããŸããã2GBãè¶
ãããã¡ã€ã«ã®å Žåããã®ãã£ãŒã«ãã®äžäœããããã1ãã«èšå®ãããªã¹ãå
ã®æ®ãã®ãã£ãŒã«ããšåãå€ãæã€å¥ã®ïŒãŸãã¯è€æ°ã®ïŒèŠçŽ ãéå§ããŠãäžçš®ã®ãã§ãŒã³ãååŸããŸãã ãã®ãã§ãŒã³ã®
Manifest []ãCountOrSizeãã£ãŒã«ãã®å€ãåèšããŠãåèšãã¡ã€ã«ãµã€ãºãèšç®ããŸãã
ãã¡ã€ã«ãµã€ãºã«ãŠã³ãã³ãŒã UINT64 CGCFFile::GetFileSize(UINT32 Item) { UINT64 res = lpManifest[Item].CountOrSize & 0x7FFFFFFF; if ((lpManifest[Item].CountOrSize & 0x80000000) != 0) { for (UINT32 i=0 ; i<pManifestHeader->NodeCount ; i++) { ManifestNode *MN = &lpManifest[Item]; if (((MN->Attributes & 0x00004000) != 0) && (MN->ParentIndex == 0xFFFFFFFF) && (MN->NextIndex == 0xFFFFFFFF) && (MN->ChildIndex == 0xFFFFFFFF) && (MN->FileId == lpManifest[Item].FileId)) { res += MN->CountOrSize << 31; break; } } } return res; }
ããã§ã¯ã4GBãè¶
ãããã¡ã€ã«ããŸã ãã£ãã·ã¥ã®äžéšã§ã¯ãªãããšãåæã«ãå°ããªãè³ãããããŠãäœæããŸãã...
ååã§ã¢ã€ãã ãæ€çŽ¢ãã
ããšãã°ããhl2 / maps / background_01.bspããšããååã®ãã¡ã€ã«ãèŠã€ããå¿
èŠããããŸãã æã£ãŠããååã¯ãã¹ãŠããªãŒåœ¢åŒã§æ ŒçŽãããŠããããããã¹ãåºåãæåã§åºåãããèŠçŽ ïŒãã®å Žåã¯ã/ãïŒã«åå²ããå¿
èŠããããŸãã 次ã«ãã«ãŒãèŠçŽ ã®åå«ãããhl2ããšããååã®èŠçŽ ãæ¢ããŸãã 圌ã«ã¯ãmapsããšããååã®èŠçŽ ãããããã®åŸã¯ãbackground_01.bspããšããååã®èŠçŽ ããããŸãã ãã®ãã¹ã¯æãæçœã§ãããéåžžã«é
ãã§ã-æååã®ãã€ãæ¯èŒãããã«ã¯ããªãŒãã¢ãŒããããŸãã ã·ã¢ãŒã³ã¹ãã
ãã®æé ãé«éåããããã«ãããã·ã¥ããŒãã«ãããããŒã«ãããŸãã
ããã·ã¥ã䜿çšããŠååã§ã¢ã€ãã ãæ€çŽ¢ããC ++ UINT32 CGCFFile::GetItem(char *Item) { int DelimiterPos = -1; for (UINT32 i=0 ; i<strlen(Item) ; i++) if (Item[i] == '\\') DelimiterPos = i; char *FileName = &Item[++DelimiterPos]; UINT32 Hash = jenkinsLookupHash2((UINT8*)FileName, strlen(FileName), 1), HashIdx = Hash % pManifestHeader->HashTableKeyCount, HashFileIdx = lpHashTableKeys[HashIdx]; if (HashFileIdx == CACHE_INVALID_ITEM) if (strcmp(LowerCase(Item), Item) != 0) { Hash = jenkinsLookupHash2((UINT8*)LowerCase(Item), strlen(FileName), 1); HashIdx = Hash % pManifestHeader->HashTableKeyCount; HashFileIdx = lpHashTableKeys[HashIdx]; } if (HashFileIdx == CACHE_INVALID_ITEM) return CACHE_INVALID_ITEM; HashFileIdx -= pManifestHeader->HashTableKeyCount; while (true) { UINT32 Value = this->lpHashTableIndices[HashFileIdx]; UINT32 FileID = Value & 0x7FFFFFFF; if (strcmp(GetItemPath(FileID), Item) == 0) return FileID; if ((Value & 0x80000000) == 0x80000000) break; HashFileIdx++; } return CACHE_INVALID_ITEM; }
ãã«ãã¡ã€ function TGCFFile.GetItemByPath(Path: string): integer; var end_block: boolean; Hash, HashIdx, HashValue: ulong; FileID, HashFileIdx: integer; PathEx: AnsiString; begin result:=-1; {$IFDEF UNICODE} PathEx:=Wide2Ansi(ExtractFileName(Path)); {$ELSE} PathEx:=ExtractFileName(Path); {$ENDIF} Hash:=jenkinsLookupHash2(@PathEx[1], Length(PathEx), 1); HashIdx:=Hash mod fManifestHeader.HashTableKeyCount; HashFileIdx:=lpHashTableKeys[HashIdx]; if HashFileIdx=-1 then begin if (LowerCase(Path)<>Path) then begin {$IFDEF UNICODE} Hash:=jenkinsLookupHash2(@LowerCaseAnsi(PathEx)[1], Length(PathEx), 1); {$ELSE} Hash:=jenkinsLookupHash2(@LowerCase(PathEx)[1], Length(PathEx), 1); {$ENDIF} HashIdx:=Hash mod fManifestHeader.HashTableKeyCount; HashFileIdx:=lpHashTableKeys[HashIdx]; if HashFileIdx=-1 then Exit; end; end; dec(HashFileIdx, fManifestHeader.HashTableKeyCount); repeat HashValue:=lpHashTableIndices[HashFileIdx]; FileID:=HashValue and $7FFFFFFF; end_block:= (HashValue and $80000000 = $80000000); if CompareStr(ItemPath[FileID], Path)=0 then begin result:=FileID; Exit; end; inc(HashFileIdx); until end_block; if (result=-1) and (LowerCase(Path)<>Path) then result:=GetItemByPath(LowerCase(Path)); end;
ã³ãŒããããããããã«ããã¡ã€ã«ãžã®ãã¹å
šäœããååã®ã¿ãååŸãããã®ããã·ã¥ãèšç®ããŸãã çµæã®æŽæ°é€ç®ã®æ®ããå€
ManifestHeader.HashTableKeyCountã§ååŸããŸããããã¯ã
0xffffffff ïŒãã®ãããªèŠçŽ ããªãå ŽåïŒãŸãã¯å€
X + ManifestHeader.HashTableKeyCountãå«ã
HashTableKeysãªã¹ãå
ã®ãšã³ããªã®çªå·ã«ãªããŸãã ããã«åºã¥ããŠã
Xãèšç®ããŸããããã¯ã
HashTableIndicesãªã¹ãå
ã®èŠçŽ ã®çªå·ã§ããã
ãããã
æ€çŽ¢å¯Ÿè±¡ã®èŠçŽ ãèŠã€ããããšãã§ããŸãã ãã®ãªã¹ãã®å€ã¯ãã¯ãšãªã§ååãæ¯èŒãããæ€çŽ¢å¯Ÿè±¡ã®ã¢ã€ãã ã瀺ããŸãã äžèŽããªãå Žåããªã¹ãã®æ¬¡ã®èŠçŽ ãååŸããèŠçŽ çªå·ã®æäžäœããããã0ãã«ãªããŸã§ç¹°ãè¿ããŸãã
æ··ä¹±ããããšãããã£ããããããã©ã®ããã«æ©èœããããç解ããŠããããã®ãããªæ··ä¹±ã®ããã«VALVEããã°ã©ããŒã責ããªããããã®æ¹æ³ã¯ãããªãŒã§ã®çŽæ¥æ€çŽ¢ãããã¯ããã«åªããŠããŸããã²ãŒã ãéå§ãããšãã®ããã©ãŒãã³ã¹ããSteam.dllã®èªå·±èšè¿°ã©ã€ãã©ãªãšãã¥ã¬ãŒã¿ã©ã€ãã©ãªãšæ¯èŒããŸãããããã«ã€ããŠã¯ãåŒãç¶ã説æããŸãã
èŠçŽ ãžã®ãã«ãã¹ãååŸãã
ãã®ã¢ã¯ã·ã§ã³ã¯ä»¥åã®ã¢ã¯ã·ã§ã³ã«ããããæ»ããŸã-ããªãŒãçµç±ããŠã«ãŒãèŠçŽ ã«ç§»åãããã¡ã€ã«ãžã®ãã¹ãååŸããå¿
èŠãããèŠçŽ ã®æ°ã
ãã¡ã€ã«ãã¹ãååŸããC ++ char *CGCFFile::GetItemPath(UINT32 Item) { size_t len = strlen(&lpNames[lpManifest[Item].NameOffset]); UINT32 Idx = lpManifest[Item].ParentIndex; while (Idx != CACHE_INVALID_ITEM) { len += strlen(&lpNames[lpManifest[Idx].NameOffset]) + 1; Idx= lpManifest[Idx].ParentIndex; } len--; char *res = new char[len+1]; memset(res, 0, len+1); size_t l = strlen(&lpNames[lpManifest[Item].NameOffset]); memcpy(&res[len-l], &lpNames[lpManifest[Item].NameOffset], l); len -= strlen(&lpNames[lpManifest[Item].NameOffset]); res[--len] = '\\'; Item = lpManifest[Item].ParentIndex; while ((Item != CACHE_INVALID_ITEM) && (Item != 0)) { l = strlen(&lpNames[lpManifest[Item].NameOffset]); memcpy(&res[len-l], &lpNames[lpManifest[Item].NameOffset], l); len -= strlen(&lpNames[lpManifest[Item].NameOffset]); res[--len] = '\\'; Item = lpManifest[Item].ParentIndex; } return res; }
ãã«ãã¡ã€ function TGCFFile.GetItemPath(Item: integer): string; var res: AnsiString; begin res:=pAnsiChar(@fNameTable[lpManifestNodes[Item].NameOffset+1]); Item:=lpManifestNodes[Item].ParentIndex; while (Item>-1) do begin res:=pAnsiChar(@fNameTable[lpManifestNodes[Item].NameOffset+1])+'\'+res; Item:=lpManifestNodes[Item].ParentIndex; end; Delete(res, 1, 1); {$IFDEF UNICODE} result:=Ansi2Wide(res); {$ELSE} result:=res; {$ENDIF} end;
Delphiã®ã³ãŒãã¯ãC ++ã®å Žåã¯std :: stringã¯ã©ã¹ã䜿çšããªãã£ããããã¯ããã«å°ãããªããŸãã-ããã«ã€ããŠã¯ç¥ããŸããã§ããã ããã«ãããã³ãŒãã¯ã¯ããã«çããªããŸã...ã¹ããªãŒã
ã¢ãŒã«ã€ãã®ãããªãã¡ã€ã«åœ¢åŒïŒä»ã®ãã¡ã€ã«ãå«ãïŒã®ã©ã€ãã©ãªãäœæãããšãã¯ããã¹ããªãŒã ã€ã³ã¹ããªãŒã ãã®ååã䜿çšããŸããããã«ãããã¢ãŒã«ã€ãå
ã®ãã¡ã€ã«ãå±éããã«éãããšãã§ããŸãã ããšãã°
ãå€ãããŒãžã§ã³ã®ãã£ãã·ã¥
half-life.gcfã«ã¯ãã¢ãŒã«ã€ãã§ãããã¡ã€ã«
pak0.pakããããŸããã ãã®çµæã
half-life.gcfãã¡ã€ã«ãit-
pak0.pakã§éããŸãã ã é çªã«ãå¿
èŠãªãã¡ã€ã«ãèªã¿ãŸãã ãããŠãããããã¹ãŠ-ã¡ã¢ãªã«ãã解åããããšãªãããã¹ãŠã®æ©èœã¯ããã¡ã€ã«ã¹ããªãŒã ïŒäœã¬ãã«ãWindowsAPIã¬ãã«ïŒã§äœæããã©ãããŒãä»ããŠå®è£
ãããŸãã
ãã£ãã·ã¥å
ã®ãã¡ã€ã«ãéãC ++ CStream *CGCFFile::OpenFile(char* FileName, UINT8 Mode) { UINT32 Item = GetItem(FileName); if (Item == CACHE_INVALID_ITEM) return NULL; if ((lpManifest[Item].Attributes & CACHE_FLAG_FILE) != CACHE_FLAG_FILE) return NULL; return OpenFile(Item, Mode); } CStream *CGCFFile::OpenFile(UINT32 Item, UINT8 Mode) { StreamData *Data = new StreamData(); memset(Data, 0, sizeof(StreamData)); Data->Handle = (handle_t)Item; Data->Package = this; Data->Size = this->GetItemSize(Item).Size; if (IsNCF) Data->FileStream = (CStream*)new CStream(MakeStr(CommonPath, GetItemPath(Item)), Mode==CACHE_OPEN_WRITE); else BuildClustersTable(Item, &Data->Sectors); return new CStream(pStreamMethods, Data); }
ãã«ãã¡ã€ function TGCFFile.OpenFile(FileName: string; Access: byte): TStream; var Item: integer; begin result:=nil; Item:=ItemByPath[FileName]; if (Item=-1) then Exit; if ((lpManifestNodes[Item].Attributes and HL_GCF_FLAG_FILE<>HL_GCF_FLAG_FILE) or (ItemSize[Item].Size=0)) then Exit; result:=OpenFile(Item, Access); end; function TGCFFile.OpenFile(Item: integer; Access: byte): TStream; var res: TStream; begin res:=TStream.CreateStreamOnStream(@StreamMethods); res.Data.fHandle:=ulong(Item); res.Data.Package:=self; res.Data.fSize:=(res.Data.Package as TGCFFile).ItemSize[Item].Size; res.Data.fPosition:=0; if (IsNCF) then begin CommonPath:=IncludeTrailingPathDelimiter(CommonPath); case Access of ACCES_READ: begin res.Data.FileStream:=TStream.CreateReadFileStream(CommonPath+ItemPath[Item]); res.Methods.fSetSiz:=StreamOnStream_SetSizeNULL; res.Methods.fWrite:=StreamOnStream_WriteNULL; end; ACCES_WRITE: begin ForceDirectories(ExtractFilePath(CommonPath+ItemPath[Item])); res.Data.FileStream:=TStream.CreateWriteFileStream(CommonPath+ItemPath[Item]); end; ACCES_READWRITE: res.Data.FileStream:=TStream.CreateReadWriteFileStream(CommonPath+ItemPath[Item]); end; res.Data.FileStream.Seek(0, spBegin); end else GCF_BuildClustersTable(Item, @res.Data.SectorsTable); result:=res; end;
ãããã£ãŠãã³ã³ãã³ãã®æäœã倧å¹
ã«ç°¡çŽ åãããŸã-ãã¡ã€ã«ãéããŠãäžå¿
èŠãªãžã§ã¹ãã£ãŒãªãã§ãã¡ã€ã«ããããŒã¿ãèªã¿åãããšãã§ããŸãã
ãã§ãã¯ãµã ãã¡ã€ã«ã®æœåº
ãã®æé ã§ã¯ãäžèšã®ã¹ããªãŒã ãç©æ¥µçã«äœ¿çšãããŸã-åºå®ãµã€ãºïŒãã§ãã¯ãµã ã®æ倧ãã©ã°ã¡ã³ããµã€ãºã¯32KbïŒã®ãã©ã°ã¡ã³ãã§ãã¡ã€ã«ãèªã¿åãããããã®ãã§ãã¯ãµã ãèšç®ããããããŒã®ããŒãã«ã®å€ãšæ¯èŒããŸãã
COPã確èªããŠãã¡ã€ã«ãæœåºããC ++ UINT64 CGCFFile::ExtractFile(UINT32 Item, char *Dest, bool IsValidation) { CStream *fileIn = this->OpenFile(Item, CACHE_OPEN_READ), *fileOut; if (fileIn == NULL) return 0; if (!IsValidation) { if (DirectoryExists(Dest)) Dest = MakeStr(IncludeTrailingPathDelimiter(Dest), GetItemName(Item)); fileOut = new CStream(Dest, true); if (fileOut->GetHandle() == INVALID_HANDLE_VALUE) return 0; fileOut->SetSize(GetItemSize(Item).Size); } UINT8 buf[CACHE_CHECKSUM_LENGTH]; UINT32 CheckSize = CACHE_CHECKSUM_LENGTH; UINT64 res = 0; while ((fileIn->Position()<fileIn->GetSize()) && (CheckSize == CACHE_CHECKSUM_LENGTH)) { if (Stop) break; UINT32 CheckIdx = lpFileIDChecksum[lpManifest[Item].FileId].FirstChecksumIndex + ((fileIn->Position() & 0xffffffffffff8000) >> 15); CheckSize = (UINT32)fileIn->Read(buf, CheckSize); UINT32 CheckFile = Checksum(buf, CheckSize), CheckFS = lpChecksum[CheckIdx]; if (CheckFile != CheckFS) { break; } else if (!IsValidation) { fileOut->Write(buf, CheckSize); } res += CheckSize; } delete fileIn; if (!IsValidation) delete fileOut; return res; }
ãã«ãã¡ã€ function TGCFFile.ExtractFile(Item: integer; Dest: string; IsValidation: boolean = false): int64; var StreamF, StreamP: TStream; CheckSize, CheckFile, CheckFS, CheckIdx: uint32_t; buf: array of byte; Size: int64; begin result:=0; StreamP:=OpenFile(Item, ACCES_READ); if (StreamP=nil) then Exit; Size:=ItemSize[Item].Size; if Assigned(OnProgress) then OnProgress(ItemPath[Item], 0, Size, Data); if Assigned(OnProgressObj) then OnProgressObj(ItemPath[Item], 0, Size, Data); StreamF:=nil; if (not IsValidation) then begin if DirectoryExists(Dest) then Dest:=IncludeTrailingPathDelimiter(Dest)+ExtractFileName(ItemName[Item]); StreamF:=TStream.CreateWriteFileStream(Dest); StreamF.Size:=ItemSize[Item].Size; if StreamF.Handle=INVALID_HANDLE_VALUE then begin StreamF.Free; Exit; end; end; SetLength(buf, HL_GCF_CHECKSUM_LENGTH); CheckSize:=HL_GCF_CHECKSUM_LENGTH; while ((StreamP.Position<StreamP.Size) and (CheckSize=HL_GCF_CHECKSUM_LENGTH)) do begin CheckIdx:=lpFileIdChecksumTableEntries[lpManifestNodes[Item].FileId].FirstChecksumIndex+ ((StreamP.Position and $ffffffffffff8000) shr 15); CheckSize:=StreamP.Read(buf[0], HL_GCF_CHECKSUM_LENGTH); CheckFile:=Checksum(@buf[0], CheckSize); CheckFS:=lpChecksumEntries[CheckIdx]; if (CheckFile<>CheckFS) and (not IgnoreCheckError) then begin if Assigned(OnError) then OnError(GetItemPath(Item), ERROR_CHECKSUM, Data); if Assigned(OnErrorObj) then OnErrorObj(GetItemPath(Item), ERROR_CHECKSUM, Data); break; end else if (not IsValidation) then StreamF.Write(buf[0], CheckSize); inc(result, CheckSize); if Assigned(OnProgress) then OnProgress('', result, Size, Data); if Assigned(OnProgressObj) then OnProgressObj('', result, Size, Data); if Stop then break; end; SetLength(buf, 0); StreamP.Free; if (not IsValidation) then StreamF.Free; end;
Delphiã®ã³ãŒãã«ã¯ãäœæ¥ã®é²è¡ç¶æ³ã衚瀺ããããã®è¿œå ã³ãŒãããããŸã
-OnProgressãOnProgressObjã³ãŒã«ããã¯é¢æ°ã®åŒã³åºãã
ãã¡ã€ã«ã®å
容ã解èªãã
å€ãã®ã²ãŒã ã¯ãªãªãŒã¹ã®å°ãåã«äºåã«ããŠã³ããŒãã§ããããããã®ãããªå Žåã®ã³ã³ãã³ãã¯å®å
šãŸãã¯éšåçã«æå·åãããŸãã ã²ãŒã ã®ãªãªãŒã¹ã«ããããã®ã³ã³ãã³ãã埩å·åããããã®ããŒãå©çšå¯èœã«ãªãã次ã®ã³ãŒãã§å®è¡ãããŸãã
ãã¡ã€ã«åŸ©å·åC ++ UCHAR IV[16] = {0}; void DecryptFileChunk(char *buf, UINT32 size, char *key) { AES_KEY aes_key; AES_set_decrypt_key((UCHAR*)key, 128, &aes_key); AES_cbc_encrypt((UCHAR*)buf, (UCHAR*)buf, size, &aes_key, IV, false); } UINT64 CGCFFile::DecryptFile(UINT32 Item, char *key) { UINT64 res = 0; CStream *str = OpenFile(Item, CACHE_OPEN_READWRITE); if (str == NULL) return 0; char buf[CACHE_CHECKSUM_LENGTH], dec[CACHE_CHECKSUM_LENGTH]; UINT32 CheckSize = CACHE_CHECKSUM_LENGTH; INT32 CompSize, UncompSize, sz; while ((str->Position() < str->GetSize()) && (CheckSize == CACHE_CHECKSUM_LENGTH)) { UINT32 CheckIdx = lpFileIDChecksum[lpManifest[Item].FileId].FirstChecksumIndex + ((str->Position() & 0xffffffffffff8000) >> 15); INT32 CheckSize = (INT32)str->Read(buf, 8); memcpy(&CompSize, &buf[0], 4); memcpy(&UncompSize, &buf[4], 4); if (((UINT32)UncompSize > pManifestHeader->CompressionBlockSize) || (CompSize > UncompSize) || (UncompSize < -1) || (CompSize < -1)) { // Chunk is not compressed CheckSize = (UINT32)str->Read(&buf[8], CACHE_CHECKSUM_LENGTH-8); DecryptFileChunk(&buf[0], CheckSize, key); } else if (((UINT32)UncompSize <= pManifestHeader->CompressionBlockSize) && (CompSize <= UncompSize) && (UncompSize > -1) || (CompSize > -1)) { // Chunk is compressed CheckSize = (UINT32)str->Read(&buf[8], UncompSize-8); INT32 CheckFile = UncompSize; if (CompSize%16 == 0) sz = CompSize; else sz = CompSize + 16 - (CompSize%16); memcpy(dec, buf, sz); DecryptFileChunk(&dec[0], sz, key); uncompress((Bytef*)&buf[0], (uLongf*)&CheckFile, (Bytef*)&dec[0], sz); } str->Seek(-CheckSize, USE_SEEK_CURRENT); str->Write(&buf[0], CheckSize); UINT32 Check1 = Checksum((UINT8*)&buf[0], CheckSize), Check2 = lpChecksum[CheckIdx]; if (Check1 != Check2) break; res += CheckSize; } lpManifest[Item].Attributes = lpManifest[Item].Attributes & (!CACHE_FLAG_ENCRYPTED); return res; }
ãã«ãã¡ã€ const IV: array[0..15] of byte = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); procedure DecryptFileChunk(buf: pByte; ChunkSize: integer; Key: Pointer); var AES: TCipher_Rijndael; src: array[0..HL_GCF_CHECKSUM_LENGTH-1] of byte; begin Move(buf^, src[0], HL_GCF_CHECKSUM_LENGTH); AES:=TCipher_Rijndael.Create(); AES.Init(Key^, 16, IV[0], 16); AES.Mode:=cmCFBx; AES.Decode(src[0], buf^, ChunkSize); AES.Free; end; function TGCFFile.DecryptFile(Item: integer; Key: Pointer): int64; var StreamP: TStream; CheckSize, CheckFile, CheckFS, CheckIdx, sz: uint32_t; buf: array of byte; dec: array[0..HL_GCF_CHECKSUM_LENGTH] of byte; CompSize, UncompSize: integer; Size: int64; begin result:=0; StreamP:=OpenFile(Item, ACCES_READWRITE); if (StreamP=nil) then Exit; Size:=ItemSize[Item].Size; if Assigned(OnProgress) then OnProgress(ItemName[Item], 0, Size, Data); if Assigned(OnProgressObj) then OnProgressObj(ItemName[Item], 0, Size, Data); SetLength(buf, HL_GCF_CHECKSUM_LENGTH); CheckSize:=HL_GCF_CHECKSUM_LENGTH; while ((StreamP.Position<StreamP.Size) and (CheckSize=HL_GCF_CHECKSUM_LENGTH)) do begin CheckIdx:=lpFileIdChecksumTableEntries[lpManifestNodes[Item].FileId].FirstChecksumIndex+ ((StreamP.Position and $ffffffffffff8000) shr 15); CheckSize:=StreamP.Read(buf[0], 8); Move(buf[0], CompSize, 4); Move(buf[4], UncompSize, 4); if (ulong(UncompSize)>fManifestHeader.CompressionBlockSize) or (CompSize>UncompSize) or (UncompSize<-1) or (CompSize<-1) then begin
ManifestHeaderã®ãã§ãã¯ãµã èšç®
ãã®å€ã®èšç®ã«ã¯ã次ã®ããããŒæ§é ã䜿çšãããŸãã- ãããã§ã¹ãããããŒ
- ãããã§ã¹ã[]
- ãã¡ã€ã«å
- HashTableKeys []
- HashTableIndices []
- MinimumFootprints []
- UserConfig []
CSãèšç®ããåã«ã次ã®ãã£ãŒã«ãããªã»ãããããŸãã- ManifestHeader.Fingerprint
- ManifestHeader.Checksum
èšç®èªäœã¯ãããã·ã¥é¢æ°ã®èšç®ã®ã·ãŒã±ã³ã¹ã«äœæžãAdler32ãã§ãã¯ïŒãã¹ãŠã®ãããã®æ§é ã®ããã®Delphi function ManifestChecksum(Header: pCache_ManifestHeader; entries, names, hashs, table, MFP, UCF: pByte): uint32_t; var tmp1, tmp2: uint32; begin tmp1:=Header.Fingerprint; tmp2:=Header.Checksum; Header.Fingerprint:=0; Header.Checksum:=0; result:=adler32(0, pAnsiChar(Header), sizeof(TCache_ManifestHeader)); result:=adler32(result, pAnsiChar(entries), sizeof(TCache_ManifestNode)*Header^.NodeCount); result:=adler32(result, pAnsiChar(names), Header^.NameSize); result:=adler32(result, pAnsiChar(hashs), sizeof(uint32)*Header^.HashTableKeyCount); result:=adler32(result, pAnsiChar(table), sizeof(uint32)*Header^.NodeCount); if Header^.NumOfMinimumFootprintFiles>0 then result:=adler32(result, pAnsiChar(MFP), sizeof(uint32)*Header^.NumOfMinimumFootprintFiles); if Header^.NumOfUserConfigFiles>0 then result:=adler32(result, pAnsiChar(UCF), sizeof(uint32)*Header^.NumOfUserConfigFiles); Header.Fingerprint:=tmp1; Header.Checksum:=tmp2; end;
ãããã«
説æãç
©éã§ããããã«ãã®èšäºã§ã¯èæ
®ãããŠããªãä»ã®æ©èœïŒã»ã¯ã¿ãŒãããã®å€æŽããã®ãããã®åæ§ç¯æãªã©ãããžãŒã»ã¯ã¿ãŒã®ããããããã®äœ¿çšãªã©ïŒã¯ããªããžããªã§èŠãããšãã§ããŸãïŒä»¥äžã§èª¬æããæ®ãã®ããã°ã©ã ãã©ã°ã¡ã³ãïŒèšäºïŒããããã®ãœãŒã¹ã³ãŒãã¯ããããžã§ã¯ãã§äœ¿çšã§ããŸãïŒèª°ãããã®ãããªçãããã®ãå¿
èŠãšããå ŽåïŒããã¹ãŠã®ãœãŒã¹ã³ãŒãã®æçµæŽæ°ã®ããããã®æ¥ä»ã¯ã2011幎ã®åŸåã§ããPSïŒãã®ã©ã€ãã©ãªãæžãããšã¯ã察象ã®ãªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã«é¢ããã©ããæžããšãã«éåžžã«åœ¹ç«ã¡ãŸããã倧åŠã§ã¯ããã¡ã€ã«ã·ã¹ãã ã®åäœïŒãã¡ã€ã«ã®äœæãæžã蟌ã¿ãèªã¿åããåé€ïŒãã·ãã¥ã¬ãŒãããå¿
èŠããããŸãããç§ã®äœåã¯æåã§ããããããããå¯äžã®äœåã§ããããããã¯ãšã»ã¯ã¿ãŒã«åå²ããããã¡ã€ã«ã·ã¹ãã ã®ã€ã¡ãŒãžã䜿çšãããŸããããã®äœæ¥ã®äžç°ãšããŠããã£ãã·ã¥ã®ããã©ã°ããŒã«ãè¿œå ããŸãã...