public static byte[] ToBytes(List <string> texts) { var writer = new DataWriter(); WriteTexts(writer, texts); return(writer.ToArray()); }
public static void WriteJH(DataWriter writer, byte[] fileData, ushort encryptKey, bool additionalLobCompression, bool noHeader = false) { if (additionalLobCompression) { var lobWriter = new DataWriter(); WriteLob(lobWriter, fileData, (uint)FileType.LOB); fileData = lobWriter.ToArray(); } var encryptedData = Compression.JH.Crypt(fileData, encryptKey); if (!noHeader) { uint header = (uint)FileType.JH | (uint)((ushort)((uint)FileType.JH >> 16) ^ encryptKey); writer.Write(header); } writer.Write(encryptedData); }
public static void WriteContainer(DataWriter writer, Dictionary <uint, byte[]> filesData, FileType fileType) { switch (fileType) { case FileType.AMNC: case FileType.AMNP: case FileType.AMBR: case FileType.AMPC: { if (filesData.Count >= 0xffff) // -1 cause JH uses the 1-based index as a word { throw new AmbermoonException(ExceptionScope.Data, $"In a container file there can only be {0xffff-1} files at max."); } if (filesData.ContainsKey(0)) { throw new AmbermoonException(ExceptionScope.Data, "The first file must have index 1 and not 0."); } var writerWithoutHeader = new DataWriter(); int totalFileNumber = (int)filesData.Keys.Max(); List <int> fileSizes = Enumerable.Repeat(0, totalFileNumber).ToList(); foreach (var file in filesData) { var fileData = file.Value; int prevOffset = writerWithoutHeader.Position; /* * AMNC | Multiple file container (data uses [JH](JH.md) encoding). The C stands for "crypted". | 0x414d4e43 ('AMNC') * AMNP | Multiple file container (data uses [JH](JH.md) encoding and the files are often [LOB](LOB.md) encoded in addition). The P stands for "packed". | 0x414d4e50 ('AMNP') * AMBR | Multiple file container (no encryption). The R stands for "raw". | 0x414d4252 ('AMNR') * AMPC | Another multiple file container (only compressed, not JH encrypted) | 0x414d5043 ('AMPC') */ if (fileType == FileType.AMNC) { WriteJH(writerWithoutHeader, fileData, (ushort)file.Key, false); } else if (fileType == FileType.AMBR) { writerWithoutHeader.Write(fileData); } else if (fileType == FileType.AMPC) { WriteLob(writerWithoutHeader, fileData); } else // AMNP { // this may be lob compressed if size is better var lobWriter = new DataWriter(); WriteLob(lobWriter, fileData, (uint)FileType.LOB); var data = lobWriter.Size - 4 < fileData.Length ? lobWriter.ToArray() : fileData; bool lob = data != fileData; // this is always JH encoded var jhWriter = new DataWriter(); byte[] header = lob ? data.Take(8).ToArray() : new byte[4] { 0, 0, 0, 0 }; byte[] encodedData = lob ? data.Skip(8).ToArray() : data; WriteJH(jhWriter, encodedData, (ushort)file.Key, false, true); writerWithoutHeader.Write(header); writerWithoutHeader.Write(encodedData); } fileSizes[(int)file.Key - 1] = writerWithoutHeader.Position - prevOffset; } writer.Write((uint)fileType); writer.Write((ushort)totalFileNumber); fileSizes.ForEach(fileSize => writer.Write((uint)fileSize)); writer.Write(writerWithoutHeader.ToArray()); } break; default: throw new AmbermoonException(ExceptionScope.Data, $"File type '{fileType}' is no container format."); } }