private static byte[] LoadArrayBytes(Stream input, ArrayHeader array, int itemSize, bool validate) { byte[] bytes; if (array.Count == 0) { bytes = Array.Empty <byte>(); } else { var size = array.Count * itemSize; if (size > int.MaxValue) { throw new FormatException(); } input.Position = array.Offset; bytes = input.ReadBytes((int)size); } if (validate == true) { var actualHash = CRC32.Compute(bytes, 0, bytes.Length); if (actualHash != array.Hash) { throw new FormatException($"array hash mismatch: {actualHash:X8} vs {array.Hash:X8}"); } } return(bytes); }
public static CacheFile Load(Stream input, bool validate) { var headerBytes = input.ReadBytes(FileHeader.Size + CacheHeader.Size); Endian endian; CacheHeader header; using (var data = new MemoryStream(headerBytes, false)) { var fileHeader = FileHeader.Read(data); if (fileHeader.Version != 13 || fileHeader.Unknown != 0) { throw new FormatException(); } endian = fileHeader.Endian; header = CacheHeader.Read(data, endian); } if (validate == true) { var deadbeefBytes = BitConverter.GetBytes(0xDEADBEEFu); Array.Copy(deadbeefBytes, 0, headerBytes, FileHeader.Size + CacheHeader.HashOffset, 4); if (CRC32.Compute(headerBytes, 0, headerBytes.Length) != header.HeaderHash) { throw new FormatException(); } } var stringBytes = LoadArrayBytes(input, header.StringData, 1, validate); string[] names, tweakDBIds, resources; using (var data = new MemoryStream(stringBytes, false)) { names = LoadStrings(input, header.NameStringOffsets, validate, data, endian); tweakDBIds = LoadStrings(input, header.TweakDBIdStringOffsets, validate, data, endian); resources = LoadStrings(input, header.ResourceStringOffsets, validate, data, endian); } var definitionHeaders = LoadArray(input, header.Definitions, DefinitionHeader.Size, validate, DefinitionHeader.Read, endian); var definitions = new Definition[definitionHeaders.Length]; for (int i = 1; i < definitionHeaders.Length; i++) { definitions[i] = DefinitionFactory.Create(definitionHeaders[i].Type); } for (int i = 1; i < definitionHeaders.Length; i++) { var definitionHeader = definitionHeaders[i]; var definition = definitions[i]; definition.Parent = definitions[definitionHeader.ParentIndex]; definition.Name = names[definitionHeader.NameIndex]; } var reader = new DefinitionReader(input, endian, definitions, names, tweakDBIds, resources); for (int i = 1; i < definitionHeaders.Length; i++) { var definitionHeader = definitionHeaders[i]; var definition = definitions[i]; input.Position = definitionHeader.DataOffset; definition.LoadPosition = input.Position; if (validate == true) { using (var data = input.ReadToMemoryStream((int)definitionHeader.DataSize)) { var slicedReader = new DefinitionReader(data, endian, definitions, names, tweakDBIds, resources); definition.Deserialize(slicedReader); if (data.Position != data.Length) { throw new FormatException(); } } } else { var expectedPosition = input.Position + definitionHeader.DataSize; definition.Deserialize(reader); if (input.Position != expectedPosition) { throw new FormatException(); } } } var instance = new CacheFile(); instance.Unknown00 = header.Unknown00; instance.Unknown04 = header.Unknown04; instance.Unknown08 = header.Unknown08; instance.Unknown10 = header.Unknown10; instance.Definitions.AddRange(definitions.Skip(1)); return(instance); }