/// <inheritdoc /> public async Task WriteHeader(DBCHeader header) { await DbcStream.FlushAsync(); //TODO: This is kinda hack, but makes things really easy for callers. Header should always go in front. DbcStream.Position = 0; byte[] bytes = Serializer.Serialize(header); await DbcStream.WriteAsync(bytes, 0, bytes.Length); await DbcStream.FlushAsync(); }
/// <inheritdoc /> public async Task WriteEntries(IReadOnlyCollection <TDbcWriteType> entries) { //The reason we don't do anything if we encounter no entires //is someone could have a partial database. It is not their fault if they don't want to //maintain some DBC types //So we must just skip writing emptyones if (entries.Count == 0) { if (Logger.IsEnabled(LogLevel.Warning)) { Logger.LogWarning($"Skipping TableType: {entries.Count} because it has no entries. If this is not desired you must populate the table somehow."); } return; } if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogDebug($"Writing: {entries.Count} Type: {typeof(TDbcWriteType).Name}"); } //TODO: Make this more efficient DbcStringDatabase stringDatabase = StringDatabaseProvider.BuildDatabase(); //We must move the writer ahead to leave room for the header //which we must write at the end. //TODO: This is a hack, we put a fake header in the stream at first so we can rewrite it later. await HeaderWriter.WriteHeader(new DBCHeader(1, 1, 1, 1)); int entrySize = await EntryWriter.WriteContents(entries); //We have to order this otherwise the strings may be out of order based on their //offset that we set the original members to. var stringCollection = stringDatabase.StringToOffsetMap .OrderBy(pair => pair.Value) .Select(s => s.Key) .ToArray(); await StringWriter.WriteStringContents(stringCollection); DBCHeader header = new DBCHeader(entries.Count, CalculateFieldCount(entrySize), entrySize, (int)stringDatabase.Currentoffset); if (Logger.IsEnabled(LogLevel.Debug)) { Logger.LogDebug($"Generating Header for Type: {typeof(TDbcWriteType).Name} with HeaderValue: {header}"); } //Now write the real header await HeaderWriter.WriteHeader(header); }
private async Task <Dictionary <uint, TDBCEntryType> > ReadDBCEntryBlock(DBCHeader header) { //Guessing the size here, no way to know. Dictionary <uint, TDBCEntryType> entryMap = new Dictionary <uint, TDBCEntryType>(header.RecordsCount); byte[] bytes = new byte[header.RecordSize * header.RecordsCount]; //Lock for safety, we don't want anyone else accessing the stream while we read it. await ReadBytesIntoArrayFromStream(bytes); DefaultStreamReaderStrategy reader = new DefaultStreamReaderStrategy(bytes); for (int i = 0; i < header.RecordsCount; i++) { TDBCEntryType entry = default(TDBCEntryType); try { entry = Serializer.Deserialize <TDBCEntryType>(reader); } catch (Exception e) { if (Logger.IsEnabled(LogLevel.Error)) { Logger.LogError($"Encountered error reading entry Type: {typeof(TDBCEntryType).Name} at Entry count: {i} Exception: {e.Message} \n\n Stack: {e.StackTrace}"); } Console.WriteLine($"Encountered error reading entry Type: {typeof(TDBCEntryType).Name} at Entry count: {i} Exception: {e.Message} \n\n Stack: {e.StackTrace}"); throw; } entryMap.Add(entry.EntryId, entry); } if (Logger.IsEnabled(LogLevel.Debug)) { Logger.LogDebug($"Finished reading entries for Type: {typeof(TDBCEntryType).Name}"); } return(entryMap); }
//TODO: Does this work for 0 length blocks? private async Task <Dictionary <uint, string> > ReadDBCStringBlock(DBCHeader header) { Dictionary <uint, string> stringMap = new Dictionary <uint, string>(1000); DBCStream.Position = header.StartStringPosition; byte[] bytes = new byte[DBCStream.Length - DBCStream.Position]; await ReadBytesIntoArrayFromStream(bytes); DefaultStreamReaderStrategyAsync stringReader = new DefaultStreamReaderStrategyAsync(bytes); for (int currentOffset = 0; currentOffset < bytes.Length;) { string readString = (await Serializer.DeserializeAsync <StringDBC>(stringReader)).StringValue; stringMap.Add((uint)currentOffset, readString); //We must move the offset forward length + null terminator currentOffset += readString.Length + 1; } return(stringMap); }