/// <summary> /// Creates a new instance of a LuigiDataBlock by inflating it from a BinaryReader. /// </summary> /// <param name="reader">The binary reader containing the data to deserialize to create the object.</param> /// <returns>A new instance of a LuigiDataBlock.</returns> /// <remarks>It is assumed that the reader is currently positioned at the beginning of a serialized LUIGI data block.</remarks> public static LuigiDataBlock Inflate(INTV.Core.Utility.BinaryReader reader) { LuigiDataBlock dataBlock = null; var blockType = (LuigiDataBlockType)reader.ReadByte(); switch (blockType) { case LuigiDataBlockType.SetScrambleKey: dataBlock = new LuigiScrambleKeyBlock(); break; case LuigiDataBlockType.MemoryMapAndPermissionsTable: dataBlock = new LuigiMemoryMapAndPermissionsTableBlock(); break; case LuigiDataBlockType.DataHunk: dataBlock = new LuigiDataHunkBlock(); break; case LuigiDataBlockType.Metadata: dataBlock = new LuigiMetadataBlock(); break; case LuigiDataBlockType.EndOfFile: dataBlock = new LuigiEndOfFileBlock(); break; default: dataBlock = new LuigiDataBlock((LuigiDataBlockType)blockType); break; } dataBlock._deserializeByteCount = BlockTypeSize; dataBlock._deserializeByteCount += dataBlock.Deserialize(reader); return(dataBlock); }
/// <inheritdoc/> protected override uint DeserializePayload(INTV.Core.Utility.BinaryReader reader) { var bytesParsed = 0u; while (bytesParsed < Length) { bytesParsed += ParseCreditRecord(reader, bytesParsed); } return(Length); }
/// <summary> /// Creates a new instance of a LuigiDataBlock by inflating it from a Stream. /// </summary> /// <param name="stream">The stream containing the data to deserialize to create the object.</param> /// <returns>A new instance of a LuigiDataBlock.</returns> public static LuigiDataBlock Inflate(System.IO.Stream stream) { LuigiDataBlock dataBlock = null; using (var reader = new INTV.Core.Utility.BinaryReader(stream)) { dataBlock = Inflate(reader); } return(dataBlock); }
/// <summary> /// Parses string metadata, checking for invalid content. /// </summary> /// <param name="reader">A binary reader to use to get the data.</param> /// <param name="payloadLength">Payload length of the metadata to parse, in bytes.</param> /// <param name="allowLineBreaks">If <c>true</c>, line breaks are allowed in the string and preserved; otherwise, line breaks are considered invalid and a string containing one is rejected.</param> /// <returns>The string as parsed. If invalid characters are found, an empty string is returned.</returns> public static string ParseStringFromMetadata(this INTV.Core.Utility.BinaryReader reader, uint payloadLength, bool allowLineBreaks) { // PCLs only support UTF-8... // LUIGI documentation indicates this could be ASCII or UTF-8 (LUIGI)... // ROM metadata spec supports UTF-8 as of jzintv version 1843 and later. Let's hope we don't run into anything *too* weird. var bytes = reader.ReadBytes((int)payloadLength); var stringResult = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length).Trim('\0'); return(stringResult); }
private uint ParseCreditRecord(INTV.Core.Utility.BinaryReader reader, uint runningTotal) { // The format of the Credits block consists of: // 1 byte bit-field describing which credits apply to the subsequent name // EITHER a 1-byte shorthand for the name (0x80-0xFF), or, a NULL-terminated UTF-8 string for the name. var bytesParsed = 0u; var creditFlags = (CreditFlags)reader.ReadByte(); ++bytesParsed; ++runningTotal; var name = string.Empty; var character = reader.ReadByte(); ++bytesParsed; ++runningTotal; if (!Authors.TryGetValue(character, out name)) { // Discard the "stuffing" byte that indicates UTF-8 or 0x01 should follow. if (character == 0x01) { character = reader.ReadByte(); ++bytesParsed; ++runningTotal; } var nameBuffer = new List <byte>(); nameBuffer.Add(character); while ((character != 0) && (runningTotal < Length)) { character = reader.ReadByte(); nameBuffer.Add(character); ++bytesParsed; ++runningTotal; } name = System.Text.Encoding.UTF8.GetString(nameBuffer.ToArray(), 0, nameBuffer.Count).Trim('\0'); } for (int i = 0; i < NumberOfCreditFlags; ++i) { var credit = (CreditFlags)(1 << i); if (creditFlags.HasFlag(credit)) { AddCredit(credit, name); } } return(bytesParsed); }
/// <inheritdoc /> protected override LuigiFileHeader GetMemo(string filePath, object data) { LuigiFileHeader luigiHeader = null; try { using (var file = StreamUtilities.OpenFileStream(filePath)) { var reader = new INTV.Core.Utility.BinaryReader(file); byte[] header = reader.ReadBytes(MagicKey.Length); if (header.SequenceEqual(MagicKey)) { file.Seek(0, System.IO.SeekOrigin.Begin); luigiHeader = LuigiFileHeader.Inflate(file); } } } catch (Exception) { // Just in case the header looks OK, but turns out to be bad. } return(luigiHeader); }
/// <summary> /// Parses a date / time value from ROM metadata. /// </summary> /// <param name="reader">A binary reader to use to get the data.</param> /// <param name="payloadLength">Payload length of the metadata to parse, in bytes.</param> /// <returns>A MetadataDateTime that contains the date and time parsed from metadata, as well as flags indicating date and time field validity and specifics.</returns> public static MetadataDateTime ParseDateTimeFromMetadata(this INTV.Core.Utility.BinaryReader reader, uint payloadLength) { var remainingPayload = (int)payloadLength; var dateTimeFlags = MetadataDateTimeFlags.None; var year = MetadataDateTime.DefaultYear; if (remainingPayload > 0) { dateTimeFlags |= MetadataDateTimeFlags.Year; year = 1900 + reader.ReadByte(); --remainingPayload; } var month = MetadataDateTime.DefaultMonth; if (remainingPayload > 0) { month = reader.ReadByte(); --remainingPayload; var monthRange = new Range <int>(1, 12); if (!monthRange.IsValueInRange(month)) { month = MetadataDateTime.DefaultMonth; } else { dateTimeFlags |= MetadataDateTimeFlags.Month; } } var day = MetadataDateTime.DefaultDay; if (remainingPayload > 0) { day = reader.ReadByte(); --remainingPayload; var dayRange = new Range <int>(1, DateTime.DaysInMonth(year, month)); if (!dateTimeFlags.HasFlag(MetadataDateTimeFlags.Month) || !dayRange.IsValueInRange(day)) { day = MetadataDateTime.DefaultDay; } else { dateTimeFlags |= MetadataDateTimeFlags.Day; } } var hour = MetadataDateTime.DefaultHour; if (remainingPayload > 0) { hour = reader.ReadByte(); --remainingPayload; var hourRange = new Range <int>(0, 23); if (!dateTimeFlags.HasFlag(MetadataDateTimeFlags.Day) || !hourRange.IsValueInRange(hour)) { hour = MetadataDateTime.DefaultHour; } else { dateTimeFlags |= MetadataDateTimeFlags.Hour; } } var minute = MetadataDateTime.DefaultMinute; if (remainingPayload > 0) { minute = reader.ReadByte(); --remainingPayload; var minuteRange = new Range <int>(0, 59); if (!dateTimeFlags.HasFlag(MetadataDateTimeFlags.Hour) || !minuteRange.IsValueInRange(minute)) { minute = MetadataDateTime.DefaultMinute; } else { dateTimeFlags |= MetadataDateTimeFlags.Minute; } } var second = MetadataDateTime.DefaultSecond; if (remainingPayload > 0) { second = reader.ReadByte(); --remainingPayload; if (dateTimeFlags.HasFlag(MetadataDateTimeFlags.Minute) && (second == 60)) { dateTimeFlags |= MetadataDateTimeFlags.LeapSecond | MetadataDateTimeFlags.Second; second = 59; } else if (!dateTimeFlags.HasFlag(MetadataDateTimeFlags.Minute) || (second > 59)) { second = MetadataDateTime.DefaultSecond; } else { dateTimeFlags |= MetadataDateTimeFlags.Second; } } var utcOffsetHours = MetadataDateTime.DefaultUtcOffsetHours; if (remainingPayload > 0) { utcOffsetHours = reader.ReadSByte(); --remainingPayload; var offsetHoursRange = new Range <int>(-12, 12); if (!dateTimeFlags.HasFlag(MetadataDateTimeFlags.Second) || !offsetHoursRange.IsValueInRange(utcOffsetHours)) { utcOffsetHours = MetadataDateTime.DefaultUtcOffsetHours; } else { dateTimeFlags |= MetadataDateTimeFlags.UtcOffset; } } var utcOffsetMinutes = MetadataDateTime.DefaultUtcOffsetMinutes; if (remainingPayload > 0) { utcOffsetMinutes = reader.ReadByte(); --remainingPayload; if (!dateTimeFlags.HasFlag(MetadataDateTimeFlags.UtcOffset) || (utcOffsetMinutes > 59)) { utcOffsetMinutes = MetadataDateTime.DefaultUtcOffsetMinutes; } else { dateTimeFlags |= MetadataDateTimeFlags.UtcOffset; } } if (remainingPayload > 0) { System.Diagnostics.Debug.WriteLine("Too many bytes left! Draining..."); reader.BaseStream.Seek(remainingPayload, System.IO.SeekOrigin.Current); } var date = DateTimeOffset.MinValue; if (dateTimeFlags != MetadataDateTimeFlags.None) { var offset = dateTimeFlags.HasFlag(MetadataDateTimeFlags.UtcOffset) ? new TimeSpan(utcOffsetHours, utcOffsetMinutes, 0) : TimeSpan.Zero; date = new DateTimeOffset(year, month, day, hour, minute, second, offset); } return(new MetadataDateTime(date, dateTimeFlags)); }
/// <summary> /// Creates a new instance of a LuigiFileHeader by inflating it from a BinaryReader. /// </summary> /// <param name="reader">The binary reader containing the data to deserialize to create the object.</param> /// <returns>A new instance of a LuigiFileHeader.</returns> public static LuigiFileHeader Inflate(INTV.Core.Utility.BinaryReader reader) { return(Inflate <LuigiFileHeader>(reader)); }