using System; using System.IO; using System.Diagnostics; namespace TwlBackupBlock { /// /// ヘッダブロックのデータ本体。 /// public class HeaderBody : AbstractBody { public const int NORMAL_HEADER_SIZE = 240; public const int EX_HEADER_SIZE = 256; public const int LEGACY_HEADER_SIZE = 160; public const int UNIQUE_ID_SIZE = 32; public const int HARDWARE_ID_SIZE = 16; public const int MAX_CONTENTS = 8; public const int NUM_OF_SECTION = 11; public const int TMD_RESERVED_SIZE = 62; public const int RESERVED_SIZE = 13; // for WithPrivateSave public const int RESERVED_SIZE_FOR_WPS = 12; // for Legacy public const int RESERVED_SIZE_FOR_LEGACY = 4; public const int NUM_OF_SECTION_LEGACY = 4; public UInt32 signature; public UInt16 companyCode; public UInt16 version; public readonly Byte[] uniqueId; public readonly Byte[] hardwareId; public UInt64 titleId; public Int64 requiredSize; public readonly UInt32[] fileSizes; public readonly UInt32[] contentId; public readonly UInt16[] contentIndex; public readonly TmdReserved tmdReserved; public Byte headerVersion; public readonly Byte[] reserved; // for WithPrivateSave public UInt32 privateSaveForWPS; public readonly Byte[] reservedForWPS; // for Legacy public UInt32 contentIdForLegacy; public UInt16 contentIndexForLegacy; private BkpType type; private int headerSize; public HeaderBody(byte[] data, BkpType type) { this.type = type; switch (this.type) { case BkpType.NORMAL: headerSize = NORMAL_HEADER_SIZE; break; case BkpType.WITH_PRIVATE_SAVE: headerSize = EX_HEADER_SIZE; break; case BkpType.LEGACY: headerSize = LEGACY_HEADER_SIZE; break; } if (data == null) { throw new ArgumentNullException("data"); } if (data.Length != headerSize) { string message = string.Format("data.Length % {0} != 0 (data.Length:{1})", headerSize, data.Length); throw new ArgumentException("data", message); } uniqueId = new Byte[UNIQUE_ID_SIZE]; hardwareId = new Byte[HARDWARE_ID_SIZE]; if (type != BkpType.LEGACY) { fileSizes = new UInt32[NUM_OF_SECTION]; contentId = new UInt32[MAX_CONTENTS]; contentIndex = new UInt16[MAX_CONTENTS]; reserved = new Byte[RESERVED_SIZE]; } else { fileSizes = new UInt32[NUM_OF_SECTION_LEGACY]; reserved = new Byte[RESERVED_SIZE_FOR_LEGACY]; } tmdReserved = new TmdReserved(); if (type == BkpType.WITH_PRIVATE_SAVE) { reservedForWPS = new Byte[RESERVED_SIZE_FOR_WPS]; } SetBytes(data); } public override byte this[int index] { get { if (index < 0 || index >= headerSize) { throw new ArgumentException("index"); } return ReadByte(index); } set { if (index < 0 || index >= headerSize) { throw new ArgumentException("index"); } WriteByte(index, value); } } public override int Length { get { return headerSize; } } /// /// 指定したバイトを取得します。 /// /// 取得したいバイトのインデックス。 /// で指定したバイト public byte ReadByte(int index) { if (index < 0 || index >= headerSize) { throw new ArgumentException("index"); } // TODO 処理が遅ければ効率化 byte[] bytes = GetBytes(); return bytes[index]; } /// /// 指定したバイトを書き換えます。 /// /// 書き換えたいバイトのインデックス。 /// 上書きに使うバイト。 public void WriteByte(int index, byte b) { if (index < 0 || index >= headerSize) { throw new ArgumentException("index"); } // TODO 処理が遅ければ効率化 byte[] bytes = GetBytes(); bytes[index] = b; SetBytes(bytes); } public override byte[] GetBytes() { byte[] bytes = new byte[headerSize]; using (var ms = new MemoryStream(bytes)) using (var bw = new BinaryWriter(ms)) { bw.Write(signature); bw.Write(companyCode); bw.Write(version); bw.Write(uniqueId); bw.Write(hardwareId); bw.Write(titleId); bw.Write(requiredSize); foreach (UInt32 e in fileSizes) { bw.Write(e); } if (type != BkpType.LEGACY) { foreach (UInt32 e in contentId) { bw.Write(e); } } else { bw.Write(contentIdForLegacy); } if (type != BkpType.LEGACY) { foreach (UInt16 e in contentIndex) { bw.Write(e); } } bw.Write(tmdReserved.GetBytes()); if (type == BkpType.LEGACY) { bw.Write(contentIndexForLegacy); } if (type != BkpType.LEGACY) { bw.Write(headerVersion); } bw.Write(reserved); if (type == BkpType.WITH_PRIVATE_SAVE) { bw.Write(privateSaveForWPS); bw.Write(reservedForWPS); } Debug.Assert(ms.Position == headerSize); } return bytes; } public override void SetBytes(byte[] bytes) { if (bytes == null) { throw new ArgumentNullException("bytes"); } if (bytes.Length != headerSize) { string message = string.Format("bytes.Length != {0} (bytes.Length:{1})", headerSize, bytes.Length); throw new ArgumentException("bytes", message); } using (var ms = new MemoryStream(bytes)) using (var br = new ExtBinaryReader(ms)) { br.Read(ref signature); br.Read(ref companyCode); br.Read(ref version); br.Read(uniqueId); br.Read(hardwareId); br.Read(ref titleId); br.Read(ref requiredSize); br.Read(fileSizes); if (type != BkpType.LEGACY) { br.Read(contentId); } else { br.Read(ref contentIdForLegacy); } if (type != BkpType.LEGACY) { br.Read(contentIndex); } { byte[] tmdReservedBytes = new byte[TMD_RESERVED_SIZE]; br.Read(tmdReservedBytes); tmdReserved.SetBytes(tmdReservedBytes); } if (type == BkpType.LEGACY) { br.Read(ref contentIndexForLegacy); } if (type != BkpType.LEGACY) { br.Read(ref headerVersion); } br.Read(reserved); if (type == BkpType.WITH_PRIVATE_SAVE) { br.Read(ref privateSaveForWPS); br.Read(reservedForWPS); } Debug.Assert(ms.Position == headerSize); } } public override object Clone() { return new HeaderBody(GetBytes(), type); } } }