1 // ApmHandler.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/CpuArch.h" 6 7 #include "../../Common/ComTry.h" 8 9 #include "../../Windows/PropVariantUtils.h" 10 11 #include "../Common/RegisterArc.h" 12 #include "../Common/StreamUtils.h" 13 14 #include "HandlerCont.h" 15 16 #define Get32(p) GetBe32a(p) 17 18 using namespace NWindows; 19 20 namespace NArchive { 21 22 namespace NDmg { 23 const char *Find_Apple_FS_Ext(const AString &name); 24 bool Is_Apple_FS_Or_Unknown(const AString &name); 25 } 26 27 namespace NApm { 28 29 static const Byte kSig0 = 'E'; 30 static const Byte kSig1 = 'R'; 31 32 static const CUInt32PCharPair k_Flags[] = 33 { 34 { 0, "VALID" }, 35 { 1, "ALLOCATED" }, 36 { 2, "IN_USE" }, 37 { 3, "BOOTABLE" }, 38 { 4, "READABLE" }, 39 { 5, "WRITABLE" }, 40 { 6, "OS_PIC_CODE" }, 41 // { 7, "OS_SPECIFIC_2" }, // "Unused" 42 { 8, "ChainCompatible" }, // "OS_SPECIFIC_1" 43 { 9, "RealDeviceDriver" }, 44 // { 10, "CanChainToNext" }, 45 { 30, "MOUNTED_AT_STARTUP" }, 46 { 31, "STARTUP" } 47 }; 48 49 #define DPME_FLAGS_VALID (1u << 0) 50 #define DPME_FLAGS_ALLOCATED (1u << 1) 51 52 static const unsigned k_Str_Size = 32; 53 54 struct CItem 55 { 56 UInt32 StartBlock; 57 UInt32 NumBlocks; 58 UInt32 Flags; // pmPartStatus 59 char Name[k_Str_Size]; 60 char Type[k_Str_Size]; 61 /* 62 UInt32 DataStartBlock; 63 UInt32 NumDataBlocks; 64 UInt32 BootStartBlock; 65 UInt32 BootSize; 66 UInt32 BootAddr; 67 UInt32 BootEntry; 68 UInt32 BootChecksum; 69 char Processor[16]; 70 */ 71 Is_Valid_and_AllocatedNArchive::NApm::CItem72 bool Is_Valid_and_Allocated() const 73 { return (Flags & (DPME_FLAGS_VALID | DPME_FLAGS_ALLOCATED)) != 0; } 74 ParseNArchive::NApm::CItem75 bool Parse(const UInt32 *p32, UInt32 &numBlocksInMap) 76 { 77 if (GetUi32a(p32) != 0x4d50) // "PM" 78 return false; 79 numBlocksInMap = Get32(p32 + 4 / 4); 80 StartBlock = Get32(p32 + 8 / 4); 81 NumBlocks = Get32(p32 + 0xc / 4); 82 Flags = Get32(p32 + 0x58 / 4); 83 memcpy(Name, p32 + 0x10 / 4, k_Str_Size); 84 memcpy(Type, p32 + 0x30 / 4, k_Str_Size); 85 /* 86 DataStartBlock = Get32(p + 0x50); 87 NumDataBlocks = Get32(p + 0x54); 88 BootStartBlock = Get32(p + 0x5c); 89 BootSize = Get32(p + 0x60); 90 BootAddr = Get32(p + 0x64); 91 if (Get32(p + 0x68) != 0) 92 return false; 93 BootEntry = Get32(p + 0x6c); 94 if (Get32(p + 0x70) != 0) 95 return false; 96 BootChecksum = Get32(p + 0x74); 97 memcpy(Processor, p32 + 0x78 / 4, 16); 98 */ 99 return true; 100 } 101 }; 102 103 104 Z7_class_CHandler_final: public CHandlerCont 105 { 106 Z7_IFACE_COM7_IMP(IInArchive_Cont) 107 108 CRecordVector<CItem> _items; 109 unsigned _blockSizeLog; 110 bool _isArc; 111 // UInt32 _numBlocks; 112 UInt64 _phySize; 113 114 UInt64 BlocksToBytes(UInt32 i) const { return (UInt64)i << _blockSizeLog; } 115 116 virtual int GetItem_ExtractInfo(UInt32 index, UInt64 &pos, UInt64 &size) const Z7_override 117 { 118 const CItem &item = _items[index]; 119 pos = BlocksToBytes(item.StartBlock); 120 size = BlocksToBytes(item.NumBlocks); 121 return NExtract::NOperationResult::kOK; 122 } 123 }; 124 125 static const UInt32 kSectorSize = 512; 126 127 // we support only 4 cluster sizes: 512, 1024, 2048, 4096 */ 128 129 API_FUNC_static_IsArc IsArc_Apm(const Byte *p, size_t size) 130 { 131 if (size < kSectorSize) 132 return k_IsArc_Res_NEED_MORE; 133 if (GetUi32(p + 12) != 0) 134 return k_IsArc_Res_NO; 135 UInt32 v = GetUi32(p); // we read as little-endian 136 v ^= kSig0 | (unsigned)kSig1 << 8; 137 if (v & ~((UInt32)0xf << 17)) 138 return k_IsArc_Res_NO; 139 if ((0x116u >> (v >> 17)) & 1) 140 return k_IsArc_Res_YES; 141 return k_IsArc_Res_NO; 142 } 143 } 144 145 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */)) 146 { 147 COM_TRY_BEGIN 148 Close(); 149 150 UInt32 buf32[kSectorSize / 4]; 151 unsigned numPadSectors, blockSizeLog_from_Header; 152 { 153 // Driver Descriptor Map (DDM) 154 RINOK(ReadStream_FALSE(stream, buf32, kSectorSize)) 155 // 8: UInt16 sbDevType : =0 (usually), =1 in Apple Mac OS X 10.3.0 iso 156 // 10: UInt16 sbDevId : =0 (usually), =1 in Apple Mac OS X 10.3.0 iso 157 // 12: UInt32 sbData : =0 158 if (buf32[3] != 0) 159 return S_FALSE; 160 UInt32 v = GetUi32a(buf32); // we read as little-endian 161 v ^= kSig0 | (unsigned)kSig1 << 8; 162 if (v & ~((UInt32)0xf << 17)) 163 return S_FALSE; 164 v >>= 16; 165 if (v == 0) 166 return S_FALSE; 167 if (v & (v - 1)) 168 return S_FALSE; 169 // v == { 16,8,4,2 } : block size (x256 bytes) 170 const unsigned a = 171 #if 1 172 (0x30210u >> v) & 3; 173 #else 174 0; // for debug : hardcoded switch to 512-bytes mode 175 #endif 176 numPadSectors = (1u << a) - 1; 177 _blockSizeLog = blockSizeLog_from_Header = 9 + a; 178 } 179 180 /* 181 some APMs (that are ".iso" macOS installation files) contain 182 (blockSizeLog == 11) in DDM header, 183 and contain 2 overlapping maps: 184 1) map for 512-bytes-step 185 2) map for 2048-bytes-step 186 512-bytes-step map is correct. 187 2048-bytes-step map can be incorrect in some cases. 188 189 macos 8 / OSX DP2 iso: 190 There is shared "hfs" item in both maps. 191 And correct (offset/size) values for "hfs" partition 192 can be calculated only in 512-bytes mode (ignoring blockSizeLog == 11). 193 But some records (Macintosh.Apple_Driver*_) 194 can be correct on both modes: 512-bytes mode / 2048-bytes-step. 195 196 macos 921 ppc / Apple Mac OS X 10.3.0 iso: 197 Both maps are correct. 198 If we use 512-bytes-step, each 4th item is (Apple_Void) with zero size. 199 And these zero size (Apple_Void) items will be first items in 2048-bytes-step map. 200 */ 201 202 // we define Z7_APM_SWITCH_TO_512_BYTES, because 203 // we want to support old MACOS APMs that contain correct value only 204 // for 512-bytes-step mode 205 #define Z7_APM_SWITCH_TO_512_BYTES 206 207 const UInt32 numBlocks_from_Header = Get32(buf32 + 1); 208 UInt32 numBlocks = 0; 209 { 210 for (unsigned k = 0; k < numPadSectors; k++) 211 { 212 RINOK(ReadStream_FALSE(stream, buf32, kSectorSize)) 213 #ifdef Z7_APM_SWITCH_TO_512_BYTES 214 if (k == 0) 215 { 216 if (GetUi32a(buf32) == 0x4d50 // "PM" 217 // && (Get32(buf32 + 0x58 / 4) & 1) // Flags::VALID 218 // some old APMs don't use VALID flag for Apple_partition_map item 219 && Get32(buf32 + 8 / 4) == 1) // StartBlock 220 { 221 // we switch the mode to 512-bytes-step map reading: 222 numPadSectors = 0; 223 _blockSizeLog = 9; 224 break; 225 } 226 } 227 #endif 228 } 229 } 230 231 for (unsigned i = 0;;) 232 { 233 #ifdef Z7_APM_SWITCH_TO_512_BYTES 234 if (i != 0 || _blockSizeLog == blockSizeLog_from_Header) 235 #endif 236 { 237 RINOK(ReadStream_FALSE(stream, buf32, kSectorSize)) 238 } 239 240 CItem item; 241 UInt32 numBlocksInMap = 0; 242 if (!item.Parse(buf32, numBlocksInMap)) 243 return S_FALSE; 244 // v24.09: we don't check that all entries have same (numBlocksInMap) values, 245 // because some APMs have different (numBlocksInMap) values, if (Apple_Void) is used. 246 if (numBlocksInMap > (1 << 8) || numBlocksInMap <= i) 247 return S_FALSE; 248 249 const UInt32 finish = item.StartBlock + item.NumBlocks; 250 if (finish < item.StartBlock) 251 return S_FALSE; 252 if (numBlocks < finish) 253 numBlocks = finish; 254 255 _items.Add(item); 256 if (numPadSectors != 0) 257 { 258 RINOK(stream->Seek(numPadSectors << 9, STREAM_SEEK_CUR, NULL)) 259 } 260 if (++i == numBlocksInMap) 261 break; 262 } 263 264 _phySize = BlocksToBytes(numBlocks); 265 // _numBlocks = numBlocks; 266 const UInt64 physSize = (UInt64)numBlocks_from_Header << blockSizeLog_from_Header; 267 if (_phySize < physSize) 268 _phySize = physSize; 269 _isArc = true; 270 _stream = stream; 271 272 return S_OK; 273 COM_TRY_END 274 } 275 276 277 Z7_COM7F_IMF(CHandler::Close()) 278 { 279 _isArc = false; 280 _phySize = 0; 281 _items.Clear(); 282 _stream.Release(); 283 return S_OK; 284 } 285 286 287 static const Byte kProps[] = 288 { 289 kpidPath, 290 kpidSize, 291 kpidOffset, 292 kpidCharacts 293 // , kpidCpu 294 }; 295 296 static const Byte kArcProps[] = 297 { 298 kpidClusterSize 299 // , kpidNumBlocks 300 }; 301 302 IMP_IInArchive_Props 303 IMP_IInArchive_ArcProps 304 305 static void GetString(AString &dest, const char *src) 306 { 307 dest.SetFrom_CalcLen(src, k_Str_Size); 308 } 309 310 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) 311 { 312 COM_TRY_BEGIN 313 NCOM::CPropVariant prop; 314 switch (propID) 315 { 316 case kpidMainSubfile: 317 { 318 int mainIndex = -1; 319 FOR_VECTOR (i, _items) 320 { 321 const CItem &item = _items[i]; 322 if (!item.Is_Valid_and_Allocated()) 323 continue; 324 AString s; 325 GetString(s, item.Type); 326 if (NDmg::Is_Apple_FS_Or_Unknown(s)) 327 { 328 if (mainIndex != -1) 329 { 330 mainIndex = -1; 331 break; 332 } 333 mainIndex = (int)i; 334 } 335 } 336 if (mainIndex != -1) 337 prop = (UInt32)(Int32)mainIndex; 338 break; 339 } 340 case kpidClusterSize: prop = (UInt32)1 << _blockSizeLog; break; 341 case kpidPhySize: prop = _phySize; break; 342 // case kpidNumBlocks: prop = _numBlocks; break; 343 344 case kpidErrorFlags: 345 { 346 UInt32 v = 0; 347 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; 348 prop = v; 349 break; 350 } 351 } 352 prop.Detach(value); 353 return S_OK; 354 COM_TRY_END 355 } 356 357 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) 358 { 359 *numItems = _items.Size(); 360 return S_OK; 361 } 362 363 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) 364 { 365 COM_TRY_BEGIN 366 NCOM::CPropVariant prop; 367 const CItem &item = _items[index]; 368 switch (propID) 369 { 370 case kpidPath: 371 { 372 AString s; 373 GetString(s, item.Name); 374 if (s.IsEmpty()) 375 s.Add_UInt32(index); 376 AString type; 377 GetString(type, item.Type); 378 { 379 const char *ext = NDmg::Find_Apple_FS_Ext(type); 380 if (ext) 381 type = ext; 382 } 383 if (!type.IsEmpty()) 384 { 385 s.Add_Dot(); 386 s += type; 387 } 388 prop = s; 389 break; 390 } 391 /* 392 case kpidCpu: 393 { 394 AString s; 395 s.SetFrom_CalcLen(item.Processor, sizeof(item.Processor)); 396 if (!s.IsEmpty()) 397 prop = s; 398 break; 399 } 400 */ 401 case kpidSize: 402 case kpidPackSize: 403 prop = BlocksToBytes(item.NumBlocks); 404 break; 405 case kpidOffset: prop = BlocksToBytes(item.StartBlock); break; 406 case kpidCharacts: FLAGS_TO_PROP(k_Flags, item.Flags, prop); break; 407 } 408 prop.Detach(value); 409 return S_OK; 410 COM_TRY_END 411 } 412 413 static const Byte k_Signature[] = { kSig0, kSig1 }; 414 415 REGISTER_ARC_I( 416 "APM", "apm", NULL, 0xD4, 417 k_Signature, 418 0, 419 0, 420 IsArc_Apm) 421 422 }} 423