| // Archive/IsoIn.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "IsoIn.h" |
| |
| #include "../../Common/StreamUtils.h" |
| |
| namespace NArchive { |
| namespace NIso { |
| |
| Byte CInArchive::ReadByte() |
| { |
| if (m_BufferPos >= BlockSize) |
| m_BufferPos = 0; |
| if (m_BufferPos == 0) |
| { |
| size_t processedSize = BlockSize; |
| if (ReadStream(_stream, m_Buffer, &processedSize) != S_OK) |
| throw 1; |
| if (processedSize != BlockSize) |
| throw 1; |
| } |
| Byte b = m_Buffer[m_BufferPos++]; |
| _position++; |
| return b; |
| } |
| |
| void CInArchive::ReadBytes(Byte *data, UInt32 size) |
| { |
| for (UInt32 i = 0; i < size; i++) |
| data[i] = ReadByte(); |
| } |
| |
| void CInArchive::Skip(size_t size) |
| { |
| while (size-- != 0) |
| ReadByte(); |
| } |
| |
| void CInArchive::SkipZeros(size_t size) |
| { |
| while (size-- != 0) |
| { |
| Byte b = ReadByte(); |
| if (b != 0) |
| throw 1; |
| } |
| } |
| |
| UInt16 CInArchive::ReadUInt16Spec() |
| { |
| UInt16 value = 0; |
| for (int i = 0; i < 2; i++) |
| value |= ((UInt16)(ReadByte()) << (8 * i)); |
| return value; |
| } |
| |
| |
| UInt16 CInArchive::ReadUInt16() |
| { |
| Byte b[4]; |
| ReadBytes(b, 4); |
| UInt32 value = 0; |
| for (int i = 0; i < 2; i++) |
| { |
| if (b[i] != b[3 - i]) |
| IncorrectBigEndian = true; |
| value |= ((UInt16)(b[i]) << (8 * i)); |
| } |
| return (UInt16)value; |
| } |
| |
| UInt32 CInArchive::ReadUInt32Le() |
| { |
| UInt32 value = 0; |
| for (int i = 0; i < 4; i++) |
| value |= ((UInt32)(ReadByte()) << (8 * i)); |
| return value; |
| } |
| |
| UInt32 CInArchive::ReadUInt32Be() |
| { |
| UInt32 value = 0; |
| for (int i = 0; i < 4; i++) |
| { |
| value <<= 8; |
| value |= ReadByte(); |
| } |
| return value; |
| } |
| |
| UInt32 CInArchive::ReadUInt32() |
| { |
| Byte b[8]; |
| ReadBytes(b, 8); |
| UInt32 value = 0; |
| for (int i = 0; i < 4; i++) |
| { |
| if (b[i] != b[7 - i]) |
| throw 1; |
| value |= ((UInt32)(b[i]) << (8 * i)); |
| } |
| return value; |
| } |
| |
| UInt32 CInArchive::ReadDigits(int numDigits) |
| { |
| UInt32 res = 0; |
| for (int i = 0; i < numDigits; i++) |
| { |
| Byte b = ReadByte(); |
| if (b < '0' || b > '9') |
| { |
| if (b == 0) // it's bug in some CD's |
| b = '0'; |
| else |
| throw 1; |
| } |
| UInt32 d = (UInt32)(b - '0'); |
| res *= 10; |
| res += d; |
| } |
| return res; |
| } |
| |
| void CInArchive::ReadDateTime(CDateTime &d) |
| { |
| d.Year = (UInt16)ReadDigits(4); |
| d.Month = (Byte)ReadDigits(2); |
| d.Day = (Byte)ReadDigits(2); |
| d.Hour = (Byte)ReadDigits(2); |
| d.Minute = (Byte)ReadDigits(2); |
| d.Second = (Byte)ReadDigits(2); |
| d.Hundredths = (Byte)ReadDigits(2); |
| d.GmtOffset = (signed char)ReadByte(); |
| } |
| |
| void CInArchive::ReadBootRecordDescriptor(CBootRecordDescriptor &d) |
| { |
| ReadBytes(d.BootSystemId, sizeof(d.BootSystemId)); |
| ReadBytes(d.BootId, sizeof(d.BootId)); |
| ReadBytes(d.BootSystemUse, sizeof(d.BootSystemUse)); |
| } |
| |
| void CInArchive::ReadRecordingDateTime(CRecordingDateTime &t) |
| { |
| t.Year = ReadByte(); |
| t.Month = ReadByte(); |
| t.Day = ReadByte(); |
| t.Hour = ReadByte(); |
| t.Minute = ReadByte(); |
| t.Second = ReadByte(); |
| t.GmtOffset = (signed char)ReadByte(); |
| } |
| |
| void CInArchive::ReadDirRecord2(CDirRecord &r, Byte len) |
| { |
| r.ExtendedAttributeRecordLen = ReadByte(); |
| if (r.ExtendedAttributeRecordLen != 0) |
| throw 1; |
| r.ExtentLocation = ReadUInt32(); |
| r.DataLength = ReadUInt32(); |
| ReadRecordingDateTime(r.DateTime); |
| r.FileFlags = ReadByte(); |
| r.FileUnitSize = ReadByte(); |
| r.InterleaveGapSize = ReadByte(); |
| r.VolSequenceNumber = ReadUInt16(); |
| Byte idLen = ReadByte(); |
| r.FileId.SetCapacity(idLen); |
| ReadBytes((Byte *)r.FileId, idLen); |
| int padSize = 1 - (idLen & 1); |
| |
| // SkipZeros(1 - (idLen & 1)); |
| Skip(1 - (idLen & 1)); // it's bug in some cd's. Must be zeros |
| |
| int curPos = 33 + idLen + padSize; |
| if (curPos > len) |
| throw 1; |
| int rem = len - curPos; |
| r.SystemUse.SetCapacity(rem); |
| ReadBytes((Byte *)r.SystemUse, rem); |
| } |
| |
| void CInArchive::ReadDirRecord(CDirRecord &r) |
| { |
| Byte len = ReadByte(); |
| // Some CDs can have incorrect value len = 48 ('0') in VolumeDescriptor. |
| // But maybe we must use real "len" for other records. |
| len = 34; |
| ReadDirRecord2(r, len); |
| } |
| |
| void CInArchive::ReadVolumeDescriptor(CVolumeDescriptor &d) |
| { |
| d.VolFlags = ReadByte(); |
| ReadBytes(d.SystemId, sizeof(d.SystemId)); |
| ReadBytes(d.VolumeId, sizeof(d.VolumeId)); |
| SkipZeros(8); |
| d.VolumeSpaceSize = ReadUInt32(); |
| ReadBytes(d.EscapeSequence, sizeof(d.EscapeSequence)); |
| d.VolumeSetSize = ReadUInt16(); |
| d.VolumeSequenceNumber = ReadUInt16(); |
| d.LogicalBlockSize = ReadUInt16(); |
| d.PathTableSize = ReadUInt32(); |
| d.LPathTableLocation = ReadUInt32Le(); |
| d.LOptionalPathTableLocation = ReadUInt32Le(); |
| d.MPathTableLocation = ReadUInt32Be(); |
| d.MOptionalPathTableLocation = ReadUInt32Be(); |
| ReadDirRecord(d.RootDirRecord); |
| ReadBytes(d.VolumeSetId, sizeof(d.VolumeSetId)); |
| ReadBytes(d.PublisherId, sizeof(d.PublisherId)); |
| ReadBytes(d.DataPreparerId, sizeof(d.DataPreparerId)); |
| ReadBytes(d.ApplicationId, sizeof(d.ApplicationId)); |
| ReadBytes(d.CopyrightFileId, sizeof(d.CopyrightFileId)); |
| ReadBytes(d.AbstractFileId, sizeof(d.AbstractFileId)); |
| ReadBytes(d.BibFileId, sizeof(d.BibFileId)); |
| ReadDateTime(d.CTime); |
| ReadDateTime(d.MTime); |
| ReadDateTime(d.ExpirationTime); |
| ReadDateTime(d.EffectiveTime); |
| d.FileStructureVersion = ReadByte(); // = 1 |
| SkipZeros(1); |
| ReadBytes(d.ApplicationUse, sizeof(d.ApplicationUse)); |
| SkipZeros(653); |
| } |
| |
| static const Byte kSig_CD001[5] = { 'C', 'D', '0', '0', '1' }; |
| |
| static const Byte kSig_NSR02[5] = { 'N', 'S', 'R', '0', '2' }; |
| static const Byte kSig_NSR03[5] = { 'N', 'S', 'R', '0', '3' }; |
| static const Byte kSig_BEA01[5] = { 'B', 'E', 'A', '0', '1' }; |
| static const Byte kSig_TEA01[5] = { 'T', 'E', 'A', '0', '1' }; |
| |
| static inline bool CheckSignature(const Byte *sig, const Byte *data) |
| { |
| for (int i = 0; i < 5; i++) |
| if (sig[i] != data[i]) |
| return false; |
| return true; |
| } |
| |
| void CInArchive::SeekToBlock(UInt32 blockIndex) |
| { |
| if (_stream->Seek((UInt64)blockIndex * VolDescs[MainVolDescIndex].LogicalBlockSize, STREAM_SEEK_SET, &_position) != S_OK) |
| throw 1; |
| m_BufferPos = 0; |
| } |
| |
| void CInArchive::ReadDir(CDir &d, int level) |
| { |
| if (!d.IsDir()) |
| return; |
| SeekToBlock(d.ExtentLocation); |
| UInt64 startPos = _position; |
| |
| bool firstItem = true; |
| for (;;) |
| { |
| UInt64 offset = _position - startPos; |
| if (offset >= d.DataLength) |
| break; |
| Byte len = ReadByte(); |
| if (len == 0) |
| continue; |
| CDir subItem; |
| ReadDirRecord2(subItem, len); |
| if (firstItem && level == 0) |
| IsSusp = subItem.CheckSusp(SuspSkipSize); |
| |
| if (!subItem.IsSystemItem()) |
| d._subItems.Add(subItem); |
| |
| firstItem = false; |
| } |
| for (int i = 0; i < d._subItems.Size(); i++) |
| ReadDir(d._subItems[i], level + 1); |
| } |
| |
| void CInArchive::CreateRefs(CDir &d) |
| { |
| if (!d.IsDir()) |
| return; |
| for (int i = 0; i < d._subItems.Size(); i++) |
| { |
| CRef ref; |
| CDir &subItem = d._subItems[i]; |
| subItem.Parent = &d; |
| ref.Dir = &d; |
| ref.Index = i; |
| Refs.Add(ref); |
| CreateRefs(subItem); |
| } |
| } |
| |
| void CInArchive::ReadBootInfo() |
| { |
| if (!_bootIsDefined) |
| return; |
| if (memcmp(_bootDesc.BootSystemId, kElToritoSpec, sizeof(_bootDesc.BootSystemId)) != 0) |
| return; |
| |
| const Byte *p = (const Byte *)_bootDesc.BootSystemUse; |
| UInt32 blockIndex = p[0] | ((UInt32)p[1] << 8) | ((UInt32)p[2] << 16) | ((UInt32)p[3] << 24); |
| SeekToBlock(blockIndex); |
| Byte b = ReadByte(); |
| if (b != NBootEntryId::kValidationEntry) |
| return; |
| { |
| CBootValidationEntry e; |
| e.PlatformId = ReadByte(); |
| if (ReadUInt16Spec() != 0) |
| throw 1; |
| ReadBytes(e.Id, sizeof(e.Id)); |
| /* UInt16 checkSum = */ ReadUInt16Spec(); |
| if (ReadByte() != 0x55) |
| throw 1; |
| if (ReadByte() != 0xAA) |
| throw 1; |
| } |
| b = ReadByte(); |
| if (b == NBootEntryId::kInitialEntryBootable || b == NBootEntryId::kInitialEntryNotBootable) |
| { |
| CBootInitialEntry e; |
| e.Bootable = (b == NBootEntryId::kInitialEntryBootable); |
| e.BootMediaType = ReadByte(); |
| e.LoadSegment = ReadUInt16Spec(); |
| e.SystemType = ReadByte(); |
| if (ReadByte() != 0) |
| throw 1; |
| e.SectorCount = ReadUInt16Spec(); |
| e.LoadRBA = ReadUInt32Le(); |
| if (ReadByte() != 0) |
| throw 1; |
| BootEntries.Add(e); |
| } |
| else |
| return; |
| } |
| |
| HRESULT CInArchive::Open2() |
| { |
| Clear(); |
| RINOK(_stream->Seek(kStartPos, STREAM_SEEK_CUR, &_position)); |
| |
| m_BufferPos = 0; |
| BlockSize = kBlockSize; |
| for (;;) |
| { |
| Byte sig[7]; |
| ReadBytes(sig, 7); |
| Byte ver = sig[6]; |
| if (!CheckSignature(kSig_CD001, sig + 1)) |
| { |
| return S_FALSE; |
| /* |
| if (sig[0] != 0 || ver != 1) |
| break; |
| if (CheckSignature(kSig_BEA01, sig + 1)) |
| { |
| } |
| else if (CheckSignature(kSig_TEA01, sig + 1)) |
| { |
| break; |
| } |
| else if (CheckSignature(kSig_NSR02, sig + 1)) |
| { |
| } |
| else |
| break; |
| SkipZeros(0x800 - 7); |
| continue; |
| */ |
| } |
| // version = 2 for ISO 9660:1999? |
| if (ver > 2) |
| throw S_FALSE; |
| |
| if (sig[0] == NVolDescType::kTerminator) |
| { |
| break; |
| // Skip(0x800 - 7); |
| // continue; |
| } |
| switch(sig[0]) |
| { |
| case NVolDescType::kBootRecord: |
| { |
| _bootIsDefined = true; |
| ReadBootRecordDescriptor(_bootDesc); |
| break; |
| } |
| case NVolDescType::kPrimaryVol: |
| case NVolDescType::kSupplementaryVol: |
| { |
| // some ISOs have two PrimaryVols. |
| CVolumeDescriptor vd; |
| ReadVolumeDescriptor(vd); |
| if (sig[0] == NVolDescType::kPrimaryVol) |
| { |
| // some burners write "Joliet" Escape Sequence to primary volume |
| memset(vd.EscapeSequence, 0, sizeof(vd.EscapeSequence)); |
| } |
| VolDescs.Add(vd); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| if (VolDescs.IsEmpty()) |
| return S_FALSE; |
| for (MainVolDescIndex = VolDescs.Size() - 1; MainVolDescIndex > 0; MainVolDescIndex--) |
| if (VolDescs[MainVolDescIndex].IsJoliet()) |
| break; |
| // MainVolDescIndex = 0; // to read primary volume |
| const CVolumeDescriptor &vd = VolDescs[MainVolDescIndex]; |
| if (vd.LogicalBlockSize != kBlockSize) |
| return S_FALSE; |
| (CDirRecord &)_rootDir = vd.RootDirRecord; |
| ReadDir(_rootDir, 0); |
| CreateRefs(_rootDir); |
| ReadBootInfo(); |
| return S_OK; |
| } |
| |
| HRESULT CInArchive::Open(IInStream *inStream) |
| { |
| _stream = inStream; |
| UInt64 pos; |
| RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &pos)); |
| RINOK(_stream->Seek(0, STREAM_SEEK_END, &_archiveSize)); |
| RINOK(_stream->Seek(pos, STREAM_SEEK_SET, &_position)); |
| HRESULT res = S_FALSE; |
| try { res = Open2(); } |
| catch(...) { Clear(); res = S_FALSE; } |
| _stream.Release(); |
| return res; |
| } |
| |
| void CInArchive::Clear() |
| { |
| IncorrectBigEndian = false; |
| Refs.Clear(); |
| _rootDir.Clear(); |
| VolDescs.Clear(); |
| _bootIsDefined = false; |
| BootEntries.Clear(); |
| SuspSkipSize = 0; |
| IsSusp = false; |
| } |
| |
| }} |