bool FastCheck() const { if (Size <= sizeof(RawHeader)) { return false; } const RawHeader& header = GetHeader(); const uint_t endOfBlock = fromLE(header.LastSrcRestBytes) + 1; const uint_t lastBytesAddr = endOfBlock - LAST_BYTES_COUNT; const uint_t bitStreamAddr = lastBytesAddr - fromLE(header.SizeOfPacked); const uint_t srcPacked = fromLE(header.SrcPacked); if (bitStreamAddr == srcPacked) { //move forward } else if (lastBytesAddr == srcPacked + 1) { //move backward } else { return false; } const std::size_t usedSize = GetUsedSize(); return Math::InRange(usedSize, sizeof(header), Size); }
ModuleTraits(const Binary::Data& data, std::size_t footerOffset) : FooterOffset(footerOffset) , Foot(footerOffset != data.Size() ? safe_ptr_cast<const Footer*>(static_cast<const uint8_t*>(data.Start()) + footerOffset) : 0) , FirstSize(Foot ? fromLE(Foot->Size1) : 0) , SecondSize(Foot ? fromLE(Foot->Size2) : 0) { }
void GetResult(Dump& data) const override { Dump rawDump; Delegate->GetResult(rawDump); Require(0 == rawDump.size() % Registers::TOTAL); const uint32_t framesCount = rawDump.size() / Registers::TOTAL; const uint_t storedRegisters = Registers::TOTAL; const String& title = Params->Title(); const String author = Params->Author(); const uint32_t headerSize = sizeof(FYMHeader) + (title.size() + 1) + (author.size() + 1); const std::size_t contentSize = framesCount * storedRegisters; Binary::DataBuilder builder(headerSize + contentSize); FYMHeader& header = builder.Add<FYMHeader>(); header.HeaderSize = fromLE(headerSize); header.FramesCount = fromLE(framesCount); header.LoopFrame = fromLE(static_cast<uint32_t>(Params->LoopFrame())); header.PSGFreq = fromLE(static_cast<uint32_t>(Params->ClockFreq())); header.IntFreq = fromLE(static_cast<uint32_t>(Time::GetFrequencyForPeriod(Params->FrameDuration()))); builder.AddCString(title); builder.AddCString(author); for (uint_t reg = 0; reg < storedRegisters; ++reg) { uint8_t* const result = static_cast<uint8_t*>(builder.Allocate(framesCount)); for (uint_t frm = 0, inOffset = reg; frm < framesCount; ++frm, inOffset += Registers::TOTAL) { result[frm] = rawDump[inOffset]; } } Dump result; builder.CaptureResult(result); Binary::Compression::Zlib::Compress(result, data); }
static void Fix(void* data, int_t delta) { T* const ptr = static_cast<T*>(data); const T val = fromLE(*ptr); const T fixedVal = static_cast<T>(val + delta); *ptr = fromLE(fixedVal); }
bool FastCheck() const { if (Size < sizeof(RawHeader)) { return false; } const RawHeader& header = GetHeader(); BOOST_STATIC_ASSERT(sizeof(header.ID) == sizeof(FDI_ID)); if (0 != std::memcmp(header.ID, FDI_ID, sizeof(FDI_ID))) { return false; } const std::size_t dataOffset = fromLE(header.DataOffset); if (dataOffset < sizeof(header) || dataOffset > Size) { return false; } const uint_t cylinders = fromLE(header.Cylinders); if (!Math::InRange(cylinders, MIN_CYLINDERS_COUNT, MAX_CYLINDERS_COUNT)) { return false; } const uint_t sides = fromLE(header.Sides); if (!Math::InRange(sides, MIN_SIDES_COUNT, MAX_SIDES_COUNT)) { return false; } return true; }
void ParseROM(Builder& target) { while (const auto size = fromLE(Stream.ReadField<uint32_t>())) { const auto offset = fromLE(Stream.ReadField<uint32_t>()); target.SetRom(offset, *Stream.ReadData(size)); } }
void ParseRegisters(Builder& target) { Stream.Seek(0x10); const auto pc = fromLE(Stream.ReadField<uint32_t>()); const auto gp = fromLE(Stream.ReadField<uint32_t>()); Dbg("PC=0x%08x GP=0x%08x", pc, gp); target.SetRegisters(pc, gp); }
void ParseStackSection(Builder& target) { Stream.Seek(0x30); const auto stackHead = fromLE(Stream.ReadField<uint32_t>()); const auto stackSize = fromLE(Stream.ReadField<uint32_t>()); Dbg("Stack %u bytes at 0x%08x", stackSize, stackHead); target.SetStackRegion(stackHead, stackSize); }
std::size_t Parse(const Binary::Container& rawData, ImageVisitor& visitor) { SourceStream stream(rawData); try { const RawHeader& header = stream.Get<RawHeader>(); const uint_t id = fromLE(header.ID); Require(id == ID_OLD || id == ID_NEW); Require(header.Sequence == 0); Require(Math::InRange<uint_t>(header.Sides, MIN_SIDES_COUNT, MAX_SIDES_COUNT)); if (header.HasComment()) { const RawComment& comment = stream.Get<RawComment>(); if (const std::size_t size = fromLE(comment.Size)) { stream.GetData(size); } } const bool compressedData = id == ID_NEW; const bool newCompression = header.Version > 20; if (compressedData) { if (!newCompression) { Dbg("Old compression is not supported."); return 0; } const std::size_t packedSize = rawData.Size() - sizeof(header); const Binary::Container::Ptr packed = rawData.GetSubcontainer(sizeof(header), packedSize); if (const Formats::Packed::Container::Ptr fullDecoded = Formats::Packed::Lha::DecodeRawDataAtLeast(*packed, COMPRESSION_ALGORITHM, MAX_IMAGE_SIZE)) { SourceStream subStream(*fullDecoded); ParseSectors(subStream, visitor); const std::size_t usedInPacked = subStream.GetOffset(); Dbg("Used %1% bytes in packed stream", usedInPacked); if (const Formats::Packed::Container::Ptr decoded = Formats::Packed::Lha::DecodeRawDataAtLeast(*packed, COMPRESSION_ALGORITHM, usedInPacked)) { const std::size_t usedSize = decoded->PackedSize(); return sizeof(header) + usedSize; } } Dbg("Failed to decode lha stream"); return 0; } else { ParseSectors(stream, visitor); } return stream.GetOffset(); } catch (const std::exception&) { return 0; } }
void ParsePositions(Builder& builder) const { std::vector<PositionEntry> positions; uint_t loop = 0; PositionEntry entry; for (std::size_t posCursor = fromLE(Source.PositionsOffset); ; ++posCursor) { Require(positions.size() <= MAX_POSITIONS_COUNT); const uint_t val = PeekByte(posCursor); if (val == 0xff) { break; } else if (val == 0xfe) { loop = positions.size(); } else if (val >= 0x60) { entry.Transposition = val - 0x60; } else { Require(0 == val % 3); entry.PatternIndex = val / 3; positions.push_back(entry); } } Require(!positions.empty()); builder.SetPositions(positions, loop); Dbg("Positions: %1% entries, loop to %2%", positions.size(), loop); }
void ParseSectors(SourceStream& stream, ImageVisitor& visitor) { for (;;) { const RawTrack& track = stream.Get<RawTrack>(); if (track.IsLast()) { break; } Require(Math::InRange<uint_t>(track.Cylinder, 0, MAX_CYLINDERS_COUNT)); for (uint_t sect = 0; sect != track.Sectors; ++sect) { const RawSector& sector = stream.Get<RawSector>(); if (sector.NoData()) { continue; } Require(Math::InRange<uint_t>(sector.Size, 0, 6)); const std::size_t sectorSize = std::size_t(128) << sector.Size; const RawData& srcDataDesc = stream.Get<RawData>(); Require(Math::InRange<uint_t>(srcDataDesc.Method, RAW_SECTOR, RLE_SECTOR)); const std::size_t dataSize = fromLE(srcDataDesc.Size) - 1; const uint8_t* const rawData = stream.GetData(dataSize); //use track parameters for layout if (!sector.NoId()) { const Formats::CHS loc(sector.Cylinder, track.Head, sector.Number); visitor.OnSector(loc, rawData, dataSize, static_cast<SectorDataType>(srcDataDesc.Method), sectorSize); } } } }
uint_t GetInteger() const { if (Type == Length) { return fromLE(DataSize); } else if (Type == Integer) { return fromLE(*safe_ptr_cast<const uint32_t*>(this + 1)); } else { assert(!"Invalid subchunk type"); return 0; } }
void ParseTextSection(Builder& target) { Stream.Seek(0x18); const auto startAddress = fromLE(Stream.ReadField<uint32_t>()); const std::size_t size = fromLE(Stream.ReadField<uint32_t>()); Dbg("Text section %u (%u in header) bytes at 0x%08x", Stream.GetRestSize(), size, startAddress); if (size) { Stream.Seek(HEADER_SIZE); target.SetTextSection(startAddress, *Stream.ReadRestData()); } else { static const Binary::DataAdapter EMPTY(nullptr, 0); target.SetTextSection(startAddress, EMPTY); } }
virtual Container::Ptr Decode(const Binary::Container& rawData) const { if (!Player->Match(rawData)) { return Container::Ptr(); } const Binary::TypedContainer typedData(rawData); const std::size_t availSize = rawData.Size(); const std::size_t playerSize = CompiledPT24::PLAYER_SIZE; const CompiledPT24::Player& rawPlayer = *typedData.GetField<CompiledPT24::Player>(0); const uint_t dataAddr = fromLE(rawPlayer.DataAddr); if (dataAddr < playerSize) { Dbg("Invalid compile addr"); return Container::Ptr(); } const CompiledPT24::RawHeader& rawHeader = *typedData.GetField<CompiledPT24::RawHeader>(playerSize); const uint_t patternsCount = CompiledPT24::GetPatternsCount(rawHeader, availSize - playerSize); if (!patternsCount) { Dbg("Invalid patterns count"); return Container::Ptr(); } const uint_t compileAddr = dataAddr - playerSize; Dbg("Detected player compiled at %1% (#%1$04x) with %2% patterns", compileAddr, patternsCount); const std::size_t modDataSize = std::min(CompiledPT24::MAX_MODULE_SIZE, availSize - playerSize); const Binary::Container::Ptr modData = rawData.GetSubcontainer(playerSize, modDataSize); const Formats::Chiptune::PatchedDataBuilder::Ptr builder = Formats::Chiptune::PatchedDataBuilder::Create(*modData); //fix samples/ornaments offsets for (uint_t idx = offsetof(CompiledPT24::RawHeader, SamplesOffsets); idx != offsetof(CompiledPT24::RawHeader, PatternsOffset); idx += 2) { builder->FixLEWord(idx, -int_t(dataAddr)); } //fix patterns offsets for (uint_t idx = fromLE(rawHeader.PatternsOffset), lim = idx + 6 * patternsCount; idx != lim; idx += 2) { builder->FixLEWord(idx, -int_t(dataAddr)); } const Binary::Container::Ptr fixedModule = builder->GetResult(); if (Formats::Chiptune::Container::Ptr fixedParsed = Decoder->Decode(*fixedModule)) { return CreatePackedContainer(fixedParsed, playerSize + fixedParsed->Size()); } Dbg("Failed to parse fixed module"); return Container::Ptr(); }
void ParseSavestate(Builder& target) { while (const auto size = fromLE(Stream.ReadField<uint32_t>())) { const auto offset = fromLE(Stream.ReadField<uint32_t>()); target.SetSaveState(offset, *Stream.ReadData(size)); } /* const auto offset = Stream.GetPosition(); Require(SAVESTATE_SIGNATURE == Stream.ReadField<SignatureType>()); const auto rdramSize = fromLE(Stream.ReadField<uint32_t>()); const auto romHeader = Stream.ReadData(0x40); Stream.Seek(offset); target.SetSaveState(*Stream.ReadData(0x175c + rdramSize)); target.SetRom(0, *romHeader); */ }
Formats::Packed::Container::Ptr DecodeNew(Binary::InputStream& stream) { const std::size_t ZX_PAGE_SIZE = 16384; const Version2_0::Header hdr = stream.ReadField<Version2_0::Header>(); const std::size_t additionalSize = fromLE(hdr.AdditionalSize); const std::size_t readAdditionalSize = sizeof(hdr) - sizeof(Version1_45::Header) - sizeof(hdr.AdditionalSize); Require(additionalSize >= readAdditionalSize); stream.ReadData(additionalSize - readAdditionalSize); const PlatformTraits traits(additionalSize, hdr.HardwareMode, hdr.Port7ffd); std::auto_ptr<Dump> res(new Dump(ZX_PAGE_SIZE * traits.PagesCount())); Dump curPage(ZX_PAGE_SIZE); for (uint_t idx = 0; idx != traits.PagesCount(); ++idx) { const bool isPageRequired = idx < traits.MinimalPagesCount(); if (!isPageRequired && stream.GetRestSize() < sizeof(Version2_0::MemoryPage)) { break; } const Version2_0::MemoryPage page = stream.ReadField<Version2_0::MemoryPage>(); const int_t pageNumber = traits.PageNumber(page.Number); const bool isPageValid = pageNumber != PlatformTraits::NO_PAGE; if (!isPageRequired && !isPageValid) { break; } Require(isPageValid); const std::size_t pageSize = fromLE(page.DataSize); const uint8_t* pageSource = 0; if (pageSize == page.UNCOMPRESSED) { pageSource = stream.ReadData(ZX_PAGE_SIZE); } else { Require(pageSize <= stream.GetRestSize()); DecodeBlock(stream, pageSize, curPage); pageSource = &curPage.front(); } std::memcpy(&res->front() + pageNumber * ZX_PAGE_SIZE, pageSource, ZX_PAGE_SIZE); } return CreatePackedContainer(res, stream.GetPosition()); }
bool FastCheck() const { if (Size < sizeof(typename Version::RawHeader)) { return false; } const typename Version::RawHeader& header = GetHeader(); const DataMovementChecker checker(fromLE(header.PackedDataSrc), fromLE(header.PackedDataDst), fromLE(header.PackedDataSize), header.PackedDataCopyDirection); if (!checker.IsValid()) { return false; } const uint_t usedSize = GetUsedSize(); if (usedSize > Size) { return false; } return true; }
bool DecodeData() { // The main concern is to decode data as much as possible, skipping defenitely invalid structure Decoded.reserve(2 * fromLE(Header.SizeOfPacked)); //assume that first byte always exists due to header format while (!Stream.Eof() && Decoded.size() < MAX_DECODED_SIZE) { const uint_t data = Stream.GetByte(); if (0x80 == data) { //exit break; } //at least one more byte required if (Stream.Eof()) { return false; } const uint_t code = data & 0xc0; if (0x80 == code) { uint_t len = data & 0x3f; assert(len); for (; len && !Stream.Eof(); --len) { Decoded.push_back(Stream.GetByte()); } if (len) { return false; } } else if (0xc0 == code) { const std::size_t len = (data & 0x3f) + 3; const uint8_t data = Stream.GetByte(); std::fill_n(std::back_inserter(Decoded), len, data); } else { const std::size_t len = ((data & 0xf0) >> 4) + 3; const uint_t offset = 256 * (data & 0x0f) + Stream.GetByte(); if (!CopyFromBack(offset, Decoded, len)) { return false; } } } while (!Stream.Eof()) { Decoded.push_back(Stream.GetByte()); } return true; }
explicit DataDecoder(const Container& container) : IsValid(container.FastCheck()) , Header(container.GetHeader()) , Stream(Header.BitStream, fromLE(Header.SizeOfPacked)) , Result(new Dump()) , Decoded(*Result) { if (IsValid && !Stream.Eof()) { IsValid = DecodeData(); } }
bool DecodeData() { const uint8_t* const rawData = static_cast<const uint8_t*>(Header.ID); const std::size_t dataOffset = fromLE(Header.DataOffset); const uint_t cylinders = fromLE(Header.Cylinders); const uint_t sides = fromLE(Header.Sides); Dump result; result.reserve(FDI_MAX_SIZE); std::size_t trackInfoOffset = sizeof(Header) + fromLE(Header.InfoSize); std::size_t rawSize = dataOffset; for (uint_t cyl = 0; cyl != cylinders; ++cyl) { for (uint_t sid = 0; sid != sides; ++sid) { if (trackInfoOffset + sizeof(RawTrack) > Limit) { return false; } const RawTrack* const trackInfo = safe_ptr_cast<const RawTrack*>(rawData + trackInfoOffset); typedef std::vector<SectorDescr> SectorDescrs; //collect sectors reference SectorDescrs sectors; sectors.reserve(trackInfo->SectorsCount); for (std::size_t secNum = 0; secNum != trackInfo->SectorsCount; ++secNum) { const RawTrack::Sector* const sector = trackInfo->Sectors + secNum; const std::size_t secSize = 128 << sector->Size; //since there's no information about head number (always 0), do not check it //assert(sector->Head == sid); if (sector->Cylinder != cyl) { return false; } const std::size_t offset = dataOffset + fromLE(sector->Offset) + fromLE(trackInfo->Offset); if (offset + secSize > Limit) { return false; } sectors.push_back(SectorDescr(sector->Number, rawData + offset, rawData + offset + secSize)); rawSize = std::max(rawSize, offset + secSize); } //sort by number std::sort(sectors.begin(), sectors.end()); //and gather data for (SectorDescrs::const_iterator it = sectors.begin(), lim = sectors.end(); it != lim; ++it) { result.insert(result.end(), it->Begin, it->End); } //calculate next track by offset trackInfoOffset += sizeof(*trackInfo) + (trackInfo->SectorsCount - 1) * sizeof(trackInfo->Sectors); } } UsedSize = rawSize; Decoded.swap(result); return true; }
void ParseSamples(const Indices& samples, Builder& builder) const { Dbg("Samples: %1% to parse", samples.Count()); const std::size_t samplesTable = fromLE(Source.SamplesOffset); for (Indices::Iterator it = samples.Items(); it; ++it) { const uint_t samIdx = *it; Dbg("Parse sample %1%", samIdx); const std::size_t samOffset = ReadWord(samplesTable, samIdx); Sample result; ParseSample(samOffset, result); builder.SetSample(samIdx, result); } }
void ParseMainPart(Builder& target) { const RawHeader& hdr = Stream.ReadField<RawHeader>(); Require(hdr.Signature == SIGNATURE); ParseID666(hdr.ID666, target); target.SetRegisters(fromLE(hdr.Regs.PC), hdr.Regs.A, hdr.Regs.X, hdr.Regs.Y, hdr.Regs.PSW, hdr.Regs.SP); target.SetRAM(hdr.RAM, sizeof(hdr.RAM)); target.SetDSPRegisters(hdr.DSPRegisters, sizeof(hdr.DSPRegisters)); if (Stream.GetRestSize() >= sizeof(ExtraRAM)) { const ExtraRAM& extra = Stream.ReadField<ExtraRAM>(); target.SetExtraRAM(extra.Data, sizeof(extra.Data)); } }
void ParseOrnaments(const Indices& ornaments, Builder& builder) const { Dbg("Ornaments: %1% to parse", ornaments.Count()); const std::size_t ornamentsTable = fromLE(Source.OrnamentsOffset); for (Indices::Iterator it = ornaments.Items(); it; ++it) { const uint_t ornIdx = *it; Dbg("Parse ornament %1%", ornIdx); const std::size_t ornOffset = ReadWord(ornamentsTable, ornIdx); Ornament result; ParseOrnament(ornOffset, result); builder.SetOrnament(ornIdx, result); } }
String GetString() const { if (Type == Asciiz) { const char* const start = safe_ptr_cast<const char*>(this + 1); const char* const end = start + fromLE(DataSize); const StringView val = end[-1] ? StringView(start, end) : StringView(start); return Strings::OptimizeAscii(val); } else { //assert(!"Invalid subchunk type"); return String(); } }
void DecodeR2P(const uint8_t* data, std::size_t size, Dump& result) { Require(size % sizeof(R2PEntry) == 0); Dump tmp; tmp.reserve(MAX_SECTOR_SIZE); for (const R2PEntry* it = safe_ptr_cast<const R2PEntry*>(data), *lim = it + size / sizeof(*it); it != lim; ++it) { const uint_t count = fromLE(it->Count); Require(count != 0); tmp.push_back(it->Data[0]); tmp.push_back(it->Data[1]); Require(CopyFromBack(sizeof(it->Data), tmp, sizeof(it->Data) * (count - 1))); } result.swap(tmp); }
Formats::Packed::Container::Ptr Version1_45::Decode(Binary::InputStream& stream) { const Version1_45::Header hdr = stream.ReadField<Version1_45::Header>(); const std::size_t restSize = stream.GetRestSize(); const std::size_t TARGET_SIZE = 49152; const uint32_t FOOTER = 0x00eded00; if (0 == (hdr.Flag1 & hdr.COMPRESSED)) { Require(restSize >= TARGET_SIZE); const Binary::Container::Ptr rest = stream.ReadRestData(); return CreatePackedContainer(rest->GetSubcontainer(0, TARGET_SIZE), sizeof(hdr) + TARGET_SIZE); } Require(restSize > sizeof(FOOTER)); std::auto_ptr<Dump> res(new Dump(TARGET_SIZE)); DecodeBlock(stream, restSize - sizeof(FOOTER), *res); const uint32_t footer = fromLE(stream.ReadField<uint32_t>()); Require(footer == FOOTER); return CreatePackedContainer(res, stream.GetPosition()); }
void ParseExtendedPart(Builder& target) { if (Stream.GetPosition() == sizeof(RawHeader) + sizeof(ExtraRAM) && Stream.GetRestSize() >= sizeof(IFFChunkHeader)) { const IFFChunkHeader& hdr = Stream.ReadField<IFFChunkHeader>(); const std::size_t size = fromLE(hdr.DataSize); if (hdr.ID == XID6 && Stream.GetRestSize() >= size) { const auto chunks = Stream.ReadData(size); ParseSubchunks(*chunks, target); } else { Dbg("Invalid IFFF chunk stored (id=%s, size=%u)", String(hdr.ID.begin(), hdr.ID.end()), size); //TODO: fix used size instead Stream.ReadRestData(); } } }
Formats::Chiptune::Container::Ptr Parse(const Binary::Container& rawData, Builder& target) { if (!FastCheck(rawData)) { return Formats::Chiptune::Container::Ptr(); } try { const std::size_t size = rawData.Size(); const uint8_t* const begin = static_cast<const uint8_t*>(rawData.Start()); const uint8_t* const end = begin + size; const Header& header = *safe_ptr_cast<const Header*>(begin); const uint_t frames = fromLE(header.Duration); target.SetFrames(frames); std::size_t usedBegin = size; std::size_t usedEnd = 0; for (uint_t reg = 0; reg != header.Buffers.size(); ++reg) { target.StartChannel(reg); const BufferDescription& buf = header.Buffers[reg]; const std::size_t offset = buf.GetAbsoluteOffset(reg); Require(offset < size); Stream stream(buf.SizeHi, begin + offset, end); ParseBuffer(frames, stream, target); usedBegin = std::min(usedBegin, offset); usedEnd = std::max<std::size_t>(usedEnd, stream.GetCursor() - begin); } const Binary::Container::Ptr subData = rawData.GetSubcontainer(0, usedEnd); return CreateCalculatingCrcContainer(subData, usedBegin, usedEnd - usedBegin); } catch (const std::exception&) { return Formats::Chiptune::Container::Ptr(); } }
std::size_t GetAbsoluteOffset(uint_t idx) const { return fromLE(Offset) + idx * sizeof(*this) + 4; }
static void ParseSubchunks(const Binary::Data& data, Builder& target) { try { Binary::TypedContainer typed(data); for (std::size_t pos = 0; pos < typed.GetSize(); ) { const SubChunkHeader* const hdr = typed.GetField<SubChunkHeader>(pos); Require(hdr != nullptr); if (hdr->ID == 0 && 0 != (pos % 4)) { //in despite of official format description, subchunks can be not aligned by 4 byte boundary ++pos; } else { Dbg("ParseSubchunk id=%u, type=%u, size=%u", uint_t(hdr->ID), uint_t(hdr->Type), fromLE(hdr->DataSize)); pos += sizeof(*hdr) + hdr->GetDataSize(); Require(pos <= typed.GetSize()); ParseSubchunk(*hdr, target); } } } catch (const std::exception&) { //ignore } }