internal static AddonInfo ReadAddonInfo(BinaryReader reader, char gmadFormatVersion) { ulong steamid = reader.ReadUInt64(); //steamid ulong timestamp = reader.ReadUInt64(); //timestamp var timeUpdated = new EpochTime(( int )timestamp).DateTime; if (gmadFormatVersion > 1) { string content = reader.ReadBootilString(); while (!string.IsNullOrEmpty(content)) { content = reader.ReadBootilString(); } } string gmadTitle = reader.ReadBootilString(); string gmadAddonJson = reader.ReadBootilString(); //gmad reads this first then parses it later to get the actual description string gmadAuthor = reader.ReadBootilString(); int gmadAddonVersion = reader.ReadInt32(); AddonInfo addonInfo = DeserializeAddonInfoCallback(gmadAddonJson); addonInfo.Title = gmadTitle; return(addonInfo); }
private static string CreateJsonDescription(AddonInfo addonInfo) { return(SerializeAddonInfoToStringCallback(new AddonInfo() { Description = addonInfo.Description, Tags = addonInfo.Tags, WorkshopType = addonInfo.WorkshopType })); }
internal static void WriteAddonInfo(BinaryWriter writer, AddonInfo addonInfo, DateTime timestamp) { writer.Write(( ulong )0); //unused steamid writer.Write(( ulong )timestamp.ToEpochTimestamp()); writer.Write(( char )0); //required content, this hasn't been worked on and I doubt it will writer.WriteBootilString(addonInfo.Title); writer.WriteBootilString(CreateJsonDescription(addonInfo)); writer.WriteBootilString("Author Name"); //unused author name writer.Write(1); //unused addon version }
private static void PopulateOldAddonInfo(AddonInfo addonInfo, AddonInfo newAddonInfo) { //populate tags description and type only addonInfo.Description = newAddonInfo.Description; addonInfo.WorkshopType = newAddonInfo.WorkshopType; if (addonInfo.Tags != null) { addonInfo.Tags.UnionWith(newAddonInfo.Tags); } else { addonInfo.Tags = new HashSet <string>(newAddonInfo.Tags); } }
/// <summary> /// Extract the given GMA inputstream /// </summary> /// <param name="inputStream">the raw stream of the gma</param> /// <param name="requestFileStream">the callback to write the output file to, return a null if the path is invalid</param> /// <param name="addonInfo"> the addon info object to feed info to, saving this is at your discretion</param> /// <returns></returns> public static async Task <bool> Extract(Stream inputStream, Func <string, Stream> requestFileStream, AddonInfo addonInfo) { inputStream.Position = 0; using (BinaryReader reader = new BinaryReader(inputStream)) { var(gmadIdent, gmadFormatVersion) = ReadHeader(reader); if (!gmadIdent.SequenceEqual(Ident) || gmadFormatVersion > Version) { return(false); } AddonInfo newAddonInfo = ReadAddonInfo(reader, gmadFormatVersion); //the term populate here is correct, we don't want to override it again as the object might be one //passed from an existing file already PopulateOldAddonInfo(addonInfo, newAddonInfo); long gmadFileblock = ReadFileList(reader, out var gmadFileEntries); int badFileCount = 0; foreach (var entry in gmadFileEntries) { var stream = requestFileStream(entry.Name); if (stream is null) { stream = requestFileStream($"badnames/{badFileCount}+.unk"); badFileCount++; } if (stream != null) { inputStream.Position = gmadFileblock + entry.Offset; await inputStream.CopyToLimitedAsync(stream, entry.Size); } } uint addoncrc = reader.ReadUInt32(); //not used during the extraction of gmas } return(true); }
public static async Task <bool> Create(Dictionary <string, Stream> files, Stream outputStream, AddonInfo addonInfo) { var orderedFiles = files.OrderBy(kv => kv.Key); using (BinaryWriter buffer = new BinaryWriter(outputStream)) { WriteHeader(buffer); WriteAddonInfo(buffer, addonInfo, DateTime.Now); await WriteFiles(buffer, outputStream, orderedFiles); //don't write CRC32 as it's not used anywhere buffer.Write(( uint )0); } return(true); }