• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ApmHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 
9 #include "../../Windows/PropVariant.h"
10 #include "../../Windows/PropVariantUtils.h"
11 
12 #include "../Common/RegisterArc.h"
13 #include "../Common/StreamUtils.h"
14 
15 #include "HandlerCont.h"
16 
17 // #define Get16(p) GetBe16(p)
18 #define Get32(p) GetBe32(p)
19 
20 using namespace NWindows;
21 
22 namespace NArchive {
23 
24 namespace NDmg {
25   const char *Find_Apple_FS_Ext(const AString &name);
26   bool Is_Apple_FS_Or_Unknown(const AString &name);
27 }
28 
29 namespace NApm {
30 
31 static const Byte kSig0 = 'E';
32 static const Byte kSig1 = 'R';
33 
34 static const CUInt32PCharPair k_Flags[] =
35 {
36   { 0, "VALID" },
37   { 1, "ALLOCATED" },
38   { 2, "IN_USE" },
39   { 3, "BOOTABLE" },
40   { 4, "READABLE" },
41   { 5, "WRITABLE" },
42   { 6, "OS_PIC_CODE" },
43   // { 7, "OS_SPECIFIC_2" }, // "Unused"
44   // { 8, "ChainCompatible" }, // "OS_SPECIFIC_1"
45   // { 9, "RealDeviceDriver" },
46   // { 10, "CanChainToNext" },
47   { 30, "MOUNTED_AT_STARTUP" },
48   { 31, "STARTUP" }
49 };
50 
51 #define DPME_FLAGS_VALID      (1u << 0)
52 #define DPME_FLAGS_ALLOCATED  (1u << 1)
53 
54 static const unsigned k_Str_Size = 32;
55 
56 struct CItem
57 {
58   UInt32 StartBlock;
59   UInt32 NumBlocks;
60   UInt32 Flags; // pmPartStatus
61   char Name[k_Str_Size];
62   char Type[k_Str_Size];
63   /*
64   UInt32 DataStartBlock;
65   UInt32 NumDataBlocks;
66   UInt32 BootStartBlock;
67   UInt32 BootSize;
68   UInt32 BootAddr;
69   UInt32 BootEntry;
70   UInt32 BootChecksum;
71   char Processor[16];
72   */
73 
Is_Valid_and_AllocatedNArchive::NApm::CItem74   bool Is_Valid_and_Allocated() const
75     { return (Flags & (DPME_FLAGS_VALID | DPME_FLAGS_ALLOCATED)) != 0; }
76 
ParseNArchive::NApm::CItem77   bool Parse(const Byte *p, UInt32 &numBlocksInMap)
78   {
79     numBlocksInMap = Get32(p + 4);
80     StartBlock = Get32(p + 8);
81     NumBlocks = Get32(p + 0xc);
82     Flags = Get32(p + 0x58);
83     memcpy(Name, p + 0x10, k_Str_Size);
84     memcpy(Type, p + 0x30, k_Str_Size);
85     if (GetUi32(p) != 0x4d50) // "PM"
86       return false;
87     /*
88     DataStartBlock = Get32(p + 0x50);
89     NumDataBlocks = Get32(p + 0x54);
90     BootStartBlock = Get32(p + 0x5c);
91     BootSize = Get32(p + 0x60);
92     BootAddr = Get32(p + 0x64);
93     if (Get32(p + 0x68) != 0)
94       return false;
95     BootEntry = Get32(p + 0x6c);
96     if (Get32(p + 0x70) != 0)
97       return false;
98     BootChecksum = Get32(p + 0x74);
99     memcpy(Processor, p + 0x78, 16);
100     */
101     return true;
102   }
103 };
104 
105 
106 Z7_class_CHandler_final: public CHandlerCont
107 {
108   Z7_IFACE_COM7_IMP(IInArchive_Cont)
109 
110   CRecordVector<CItem> _items;
111   unsigned _blockSizeLog;
112   UInt32 _numBlocks;
113   UInt64 _phySize;
114   bool _isArc;
115 
116   UInt64 BlocksToBytes(UInt32 i) const { return (UInt64)i << _blockSizeLog; }
117 
118   virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const Z7_override
119   {
120     const CItem &item = _items[index];
121     pos = BlocksToBytes(item.StartBlock);
122     size = BlocksToBytes(item.NumBlocks);
123     return NExtract::NOperationResult::kOK;
124   }
125 };
126 
127 static const UInt32 kSectorSize = 512;
128 
129 // we support only 4 cluster sizes: 512, 1024, 2048, 4096 */
130 
131 API_FUNC_static_IsArc IsArc_Apm(const Byte *p, size_t size)
132 {
133   if (size < kSectorSize)
134     return k_IsArc_Res_NEED_MORE;
135   if (GetUi64(p + 8) != 0)
136     return k_IsArc_Res_NO;
137   UInt32 v = GetUi32(p); // we read as little-endian
138   v ^= (kSig0 | (unsigned)kSig1 << 8);
139   if ((v & ~((UInt32)0xf << 17)))
140     return k_IsArc_Res_NO;
141   if ((0x116u >> (v >> 17)) & 1)
142     return k_IsArc_Res_YES;
143   return k_IsArc_Res_NO;
144 }
145 }
146 
147 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */))
148 {
149   COM_TRY_BEGIN
150   Close();
151 
152   Byte buf[kSectorSize];
153   unsigned numSectors_in_Cluster;
154   {
155     RINOK(ReadStream_FALSE(stream, buf, kSectorSize))
156     if (GetUi64(buf + 8) != 0)
157       return S_FALSE;
158     UInt32 v = GetUi32(buf); // we read as little-endian
159     v ^= (kSig0 | (unsigned)kSig1 << 8);
160     if ((v & ~((UInt32)0xf << 17)))
161       return S_FALSE;
162     v >>= 16;
163     if (v == 0)
164       return S_FALSE;
165     if (v & (v - 1))
166       return S_FALSE;
167     const unsigned a = (0x30210u >> v) & 3;
168     // a = 0; // for debug
169     numSectors_in_Cluster = 1u << a;
170     _blockSizeLog = 9 + a;
171   }
172 
173   UInt32 numBlocks = Get32(buf + 4);
174   _numBlocks = numBlocks;
175 
176   {
177     for (unsigned k = numSectors_in_Cluster; --k != 0;)
178     {
179       RINOK(ReadStream_FALSE(stream, buf, kSectorSize))
180     }
181   }
182 
183   UInt32 numBlocksInMap = 0;
184 
185   for (unsigned i = 0;;)
186   {
187     RINOK(ReadStream_FALSE(stream, buf, kSectorSize))
188 
189     CItem item;
190 
191     UInt32 numBlocksInMap2 = 0;
192     if (!item.Parse(buf, numBlocksInMap2))
193       return S_FALSE;
194     if (i == 0)
195     {
196       numBlocksInMap = numBlocksInMap2;
197       if (numBlocksInMap > (1 << 8) || numBlocksInMap == 0)
198         return S_FALSE;
199     }
200     else if (numBlocksInMap2 != numBlocksInMap)
201       return S_FALSE;
202 
203     const UInt32 finish = item.StartBlock + item.NumBlocks;
204     if (finish < item.StartBlock)
205       return S_FALSE;
206     if (numBlocks < finish)
207         numBlocks = finish;
208 
209     _items.Add(item);
210     for (unsigned k = numSectors_in_Cluster; --k != 0;)
211     {
212       RINOK(ReadStream_FALSE(stream, buf, kSectorSize))
213     }
214     if (++i == numBlocksInMap)
215       break;
216   }
217 
218   _phySize = BlocksToBytes(numBlocks);
219   _isArc = true;
220   _stream = stream;
221 
222   return S_OK;
223   COM_TRY_END
224 }
225 
226 
227 Z7_COM7F_IMF(CHandler::Close())
228 {
229   _isArc = false;
230   _phySize = 0;
231   _items.Clear();
232   _stream.Release();
233   return S_OK;
234 }
235 
236 
237 static const Byte kProps[] =
238 {
239   kpidPath,
240   kpidSize,
241   kpidOffset,
242   kpidCharacts
243 };
244 
245 static const Byte kArcProps[] =
246 {
247   kpidClusterSize,
248   kpidNumBlocks
249 };
250 
251 IMP_IInArchive_Props
252 IMP_IInArchive_ArcProps
253 
254 static AString GetString(const char *s)
255 {
256   AString res;
257   res.SetFrom_CalcLen(s, k_Str_Size);
258   return res;
259 }
260 
261 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
262 {
263   COM_TRY_BEGIN
264   NCOM::CPropVariant prop;
265   switch (propID)
266   {
267     case kpidMainSubfile:
268     {
269       int mainIndex = -1;
270       FOR_VECTOR (i, _items)
271       {
272         const CItem &item = _items[i];
273         if (!item.Is_Valid_and_Allocated())
274           continue;
275         AString s (GetString(item.Type));
276         if (NDmg::Is_Apple_FS_Or_Unknown(s))
277         {
278           if (mainIndex != -1)
279           {
280             mainIndex = -1;
281             break;
282           }
283           mainIndex = (int)i;
284         }
285       }
286       if (mainIndex != -1)
287         prop = (UInt32)(Int32)mainIndex;
288       break;
289     }
290     case kpidClusterSize: prop = (UInt32)1 << _blockSizeLog; break;
291     case kpidPhySize: prop = _phySize; break;
292     case kpidNumBlocks: prop = _numBlocks; break;
293 
294     case kpidErrorFlags:
295     {
296       UInt32 v = 0;
297       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
298       prop = v;
299       break;
300     }
301   }
302   prop.Detach(value);
303   return S_OK;
304   COM_TRY_END
305 }
306 
307 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
308 {
309   *numItems = _items.Size();
310   return S_OK;
311 }
312 
313 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
314 {
315   COM_TRY_BEGIN
316   NCOM::CPropVariant prop;
317   const CItem &item = _items[index];
318   switch (propID)
319   {
320     case kpidPath:
321     {
322       AString s (GetString(item.Name));
323       if (s.IsEmpty())
324         s.Add_UInt32(index);
325       AString type (GetString(item.Type));
326       {
327         const char *ext = NDmg::Find_Apple_FS_Ext(type);
328         if (ext)
329           type = ext;
330       }
331       if (!type.IsEmpty())
332       {
333         s.Add_Dot();
334         s += type;
335       }
336       prop = s;
337       break;
338     }
339     case kpidSize:
340     case kpidPackSize:
341       prop = BlocksToBytes(item.NumBlocks);
342       break;
343     case kpidOffset: prop = BlocksToBytes(item.StartBlock); break;
344     case kpidCharacts: FLAGS_TO_PROP(k_Flags, item.Flags, prop); break;
345   }
346   prop.Detach(value);
347   return S_OK;
348   COM_TRY_END
349 }
350 
351 static const Byte k_Signature[] = { kSig0, kSig1 };
352 
353 REGISTER_ARC_I(
354   "APM", "apm", NULL, 0xD4,
355   k_Signature,
356   0,
357   0,
358   IsArc_Apm)
359 
360 }}
361