1 /*
2 * HLLib
3 * Copyright (C) 2006-2010 Ryan Gregg
4
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later
9 * version.
10 */
11
12 #include "HLLib.h"
13 #include "XZPFile.h"
14 #include "Streams.h"
15
16 using namespace HLLib;
17
18 const char *CXZPFile::lpAttributeNames[] = { "Version", "Preload Bytes" };
19 const char *CXZPFile::lpItemAttributeNames[] = { "Created", "Preload Bytes" };
20
CXZPFile()21 CXZPFile::CXZPFile() : CPackage(), pHeaderView(0), pDirectoryEntryView(0), pDirectoryItemView(0), pFooterView(0), pHeader(0), lpDirectoryEntries(0), lpPreloadDirectoryEntries(0), lpPreloadDirectoryMappings(0), lpDirectoryItems(0), pFooter(0)
22 {
23
24 }
25
~CXZPFile()26 CXZPFile::~CXZPFile()
27 {
28 this->Close();
29 }
30
GetType() const31 HLPackageType CXZPFile::GetType() const
32 {
33 return HL_PACKAGE_XZP;
34 }
35
GetExtension() const36 const hlChar *CXZPFile::GetExtension() const
37 {
38 return "xzp";
39 }
40
GetDescription() const41 const hlChar *CXZPFile::GetDescription() const
42 {
43 return "XBox Package File";
44 }
45
MapDataStructures()46 hlBool CXZPFile::MapDataStructures()
47 {
48 if(sizeof(XZPHeader) > this->pMapping->GetMappingSize())
49 {
50 LastError.SetErrorMessage("Invalid file: the file map is too small for it's header.");
51 return hlFalse;
52 }
53
54 if(!this->pMapping->Map(this->pHeaderView, 0, sizeof(XZPHeader)))
55 {
56 return hlFalse;
57 }
58 this->pHeader = (XZPHeader *)this->pHeaderView->GetView();
59
60 if(memcmp(this->pHeader->lpSignature, "piZx", 4) != 0)
61 {
62 LastError.SetErrorMessage("Invalid file: the file's header signature does not match.");
63 return hlFalse;
64 }
65
66 if(this->pHeader->uiVersion != 6)
67 {
68 LastError.SetErrorMessageFormated("Invalid XZP version (v%u): you have a version of a XZP file that HLLib does not know how to read. Check for product updates.", this->pHeader->uiVersion);
69 return hlFalse;
70 }
71
72 if(this->pHeader->uiHeaderLength != sizeof(XZPHeader))
73 {
74 LastError.SetErrorMessage("Invalid file: the file's header size does not match.");
75 return hlFalse;
76 }
77
78 if(!this->pMapping->Map(this->pDirectoryEntryView, sizeof(XZPHeader), this->pHeader->uiPreloadBytes ? (this->pHeader->uiDirectoryEntryCount + this->pHeader->uiPreloadDirectoryEntryCount) * sizeof(XZPDirectoryEntry) + this->pHeader->uiDirectoryEntryCount * sizeof(XZPDirectoryMapping) : this->pHeader->uiDirectoryEntryCount * sizeof(XZPDirectoryEntry)))
79 {
80 return hlFalse;
81 }
82 this->lpDirectoryEntries = (XZPDirectoryEntry *)this->pDirectoryEntryView->GetView();
83 this->lpPreloadDirectoryEntries = this->pHeader->uiPreloadBytes ? this->lpDirectoryEntries + this->pHeader->uiDirectoryEntryCount : 0;
84 this->lpPreloadDirectoryMappings = this->pHeader->uiPreloadBytes ? (XZPDirectoryMapping *)(this->lpPreloadDirectoryEntries + this->pHeader->uiPreloadDirectoryEntryCount) : 0;
85
86 if(this->pHeader->uiDirectoryItemCount != 0)
87 {
88 if(!this->pMapping->Map(this->pDirectoryItemView, this->pHeader->uiDirectoryItemOffset, this->pHeader->uiDirectoryItemLength))
89 {
90 return hlFalse;
91 }
92 this->lpDirectoryItems = (XZPDirectoryItem *)this->pDirectoryItemView->GetView();
93 }
94
95 if(!this->pMapping->Map(this->pFooterView, this->pMapping->GetMappingSize() - sizeof(XZPFooter), sizeof(XZPFooter)))
96 {
97 return hlFalse;
98 }
99 this->pFooter = (XZPFooter *)this->pFooterView->GetView();
100
101 if(memcmp(this->pFooter->lpSignature, "tFzX", 4) != 0)
102 {
103 LastError.SetErrorMessage("Invalid file: the file's footer signature does not match.");
104 return hlFalse;
105 }
106
107 if(this->pFooter->uiFileLength != this->pMapping->GetMappingSize())
108 {
109 LastError.SetErrorMessage("Invalid file: the file map is not within mapping bounds.");
110 return hlFalse;
111 }
112
113 return hlTrue;
114 }
115
UnmapDataStructures()116 hlVoid CXZPFile::UnmapDataStructures()
117 {
118 this->pFooter = 0;
119 this->pMapping->Unmap(this->pFooterView);
120
121 this->lpDirectoryItems = 0;
122 this->pMapping->Unmap(this->pDirectoryItemView);
123
124 this->lpDirectoryEntries = 0;
125 this->lpPreloadDirectoryEntries = 0;
126 this->lpPreloadDirectoryMappings = 0;
127 this->pMapping->Unmap(this->pDirectoryEntryView);
128
129 this->pHeader = 0;
130 this->pMapping->Unmap(this->pHeaderView);
131 }
132
CreateRoot()133 CDirectoryFolder *CXZPFile::CreateRoot()
134 {
135 CDirectoryFolder *pRoot = new CDirectoryFolder(this);
136
137 if(this->pHeader->uiDirectoryItemCount != 0)
138 {
139 // Loop through each file in the XZP file.
140 for(hlUInt i = 0; i < this->pHeader->uiDirectoryEntryCount; i++)
141 {
142 // Find it's info (file name).
143 for(hlUInt j = 0; j < this->pHeader->uiDirectoryItemCount; j++)
144 {
145 if(this->lpDirectoryEntries[i].uiFileNameCRC == this->lpDirectoryItems[j].uiFileNameCRC)
146 {
147 hlChar lpFileName[256];
148 strncpy(lpFileName, (hlChar *)this->lpDirectoryItems + this->lpDirectoryItems[j].uiNameOffset - this->pHeader->uiDirectoryItemOffset, sizeof(lpFileName));
149 lpFileName[sizeof(lpFileName) - 1] = '\0';
150
151 // Check if we have just a file, or if the file has directories we need to create.
152 if(strchr(lpFileName, '/') == 0 && strchr(lpFileName, '\\') == 0)
153 {
154 pRoot->AddFile(lpFileName, i);
155 }
156 else
157 {
158 // Tokenize the file path and create the directories.
159 CDirectoryFolder *pInsertFolder = pRoot;
160
161 hlChar lpTemp[256] = "";
162 hlChar *lpToken = strtok(lpFileName, "/\\");
163 while(lpToken != 0)
164 {
165 strcpy(lpTemp, lpToken);
166
167 lpToken = strtok(0, "/\\");
168
169 if(lpToken != 0)
170 {
171 // Check if the directory exists.
172 CDirectoryItem *pItem = pInsertFolder->GetItem(lpTemp);
173 if(pItem == 0 || pItem->GetType() == HL_ITEM_FILE)
174 {
175 // It doesn't, create it.
176 pInsertFolder = pInsertFolder->AddFolder(lpTemp);
177 }
178 else
179 {
180 // It does, use it.
181 pInsertFolder = static_cast<CDirectoryFolder *>(pItem);
182 }
183 }
184 }
185
186 // The file name is the last token, add it.
187 pInsertFolder->AddFile(lpTemp, i);
188 }
189 break;
190 }
191 }
192 }
193 }
194 else
195 {
196 // No file name information, just file name CRCs.
197 const hlChar lpLookup[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
198 for(hlUInt i = 0; i < this->pHeader->uiDirectoryEntryCount; i++)
199 {
200 hlChar lpTemp[16];
201
202 hlChar* lpName = lpTemp;
203 for(hlByte *lpCRC = (hlByte *)&this->lpDirectoryEntries[i].uiFileNameCRC; lpCRC < (hlByte *)&this->lpDirectoryEntries[i].uiFileNameCRC + sizeof(hlUInt); lpCRC++)
204 {
205 *lpName++ = lpLookup[(hlByte)(*lpCRC >> 4)];
206 *lpName++ = lpLookup[(hlByte)(*lpCRC & 0x0F)];
207 }
208 *lpName = '\0';
209
210 pRoot->AddFile(lpTemp, i);
211 }
212 }
213
214 return pRoot;
215 }
216
GetAttributeCountInternal() const217 hlUInt CXZPFile::GetAttributeCountInternal() const
218 {
219 return HL_XZP_PACKAGE_COUNT;
220 }
221
GetAttributeNameInternal(HLPackageAttribute eAttribute) const222 const hlChar *CXZPFile::GetAttributeNameInternal(HLPackageAttribute eAttribute) const
223 {
224 if(eAttribute < HL_XZP_PACKAGE_COUNT)
225 {
226 return this->lpAttributeNames[eAttribute];
227 }
228
229 return 0;
230 }
231
GetAttributeInternal(HLPackageAttribute eAttribute,HLAttribute & Attribute) const232 hlBool CXZPFile::GetAttributeInternal(HLPackageAttribute eAttribute, HLAttribute &Attribute) const
233 {
234 switch(eAttribute)
235 {
236 case HL_XZP_PACKAGE_VERSION:
237 hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiVersion, hlFalse);
238 return hlTrue;
239 case HL_XZP_PACKAGE_PRELOAD_BYTES:
240 hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiPreloadBytes, hlFalse);
241 return hlTrue;
242 default:
243 return hlFalse;
244 }
245 }
246
GetItemAttributeCountInternal() const247 hlUInt CXZPFile::GetItemAttributeCountInternal() const
248 {
249 return HL_XZP_ITEM_COUNT;
250 }
251
GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const252 const hlChar *CXZPFile::GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const
253 {
254 if(eAttribute < HL_XZP_ITEM_COUNT)
255 {
256 return this->lpItemAttributeNames[eAttribute];
257 }
258
259 return 0;
260 }
261
GetItemAttributeInternal(const CDirectoryItem * pItem,HLPackageAttribute eAttribute,HLAttribute & Attribute) const262 hlBool CXZPFile::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
263 {
264 switch(pItem->GetType())
265 {
266 case HL_ITEM_FILE:
267 {
268 const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
269 const XZPDirectoryEntry *pDirectoryEntry = this->lpDirectoryEntries + pFile->GetID();
270 switch(eAttribute)
271 {
272 case HL_XZP_ITEM_CREATED:
273 {
274 for(hlUInt i = 0; i < this->pHeader->uiDirectoryItemCount; i++)
275 {
276 if(this->lpDirectoryItems[i].uiFileNameCRC == pDirectoryEntry->uiFileNameCRC)
277 {
278 time_t Time = (time_t)this->lpDirectoryItems[i].uiTimeCreated;
279 tm *pTime = localtime(&Time);
280
281 hlChar lpTime[128];
282 strftime(lpTime, sizeof(lpTime), "%c", pTime);
283
284 hlAttributeSetString(&Attribute, this->lpItemAttributeNames[eAttribute], lpTime);
285 return hlTrue;
286 }
287 }
288 break;
289 }
290 case HL_XZP_ITEM_PRELOAD_BYTES:
291 {
292 hlUInt uiSize = 0;
293 if(this->lpPreloadDirectoryMappings != 0)
294 {
295 hlUInt16 uiIndex = this->lpPreloadDirectoryMappings[pFile->GetID()].uiPreloadDirectoryEntryIndex;
296 if(uiIndex != 0xffff && this->lpPreloadDirectoryEntries[uiIndex].uiFileNameCRC == pDirectoryEntry->uiFileNameCRC)
297 {
298 uiSize = this->lpPreloadDirectoryEntries[uiIndex].uiEntryLength;
299 }
300 }
301 hlAttributeSetUnsignedInteger(&Attribute, this->lpItemAttributeNames[eAttribute], uiSize, hlFalse);
302 return hlTrue;
303 }
304 }
305 break;
306 }
307 }
308
309 return hlFalse;
310 }
311
GetFileSizeInternal(const CDirectoryFile * pFile,hlUInt & uiSize) const312 hlBool CXZPFile::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
313 {
314 uiSize = this->lpDirectoryEntries[pFile->GetID()].uiEntryLength;
315
316 return hlTrue;
317 }
318
GetFileSizeOnDiskInternal(const CDirectoryFile * pFile,hlUInt & uiSize) const319 hlBool CXZPFile::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
320 {
321 uiSize = this->lpDirectoryEntries[pFile->GetID()].uiEntryLength;
322
323 return hlTrue;
324 }
325
CreateStreamInternal(const CDirectoryFile * pFile,Streams::IStream * & pStream) const326 hlBool CXZPFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
327 {
328 const XZPDirectoryEntry *pDirectoryEntry = this->lpDirectoryEntries + pFile->GetID();
329
330 pStream = new Streams::CMappingStream(*this->pMapping, pDirectoryEntry->uiEntryOffset, pDirectoryEntry->uiEntryLength);
331
332 return hlTrue;
333 }
334