Skip to content

Commit

Permalink
reduce loops in texture replacement using dictionary dictionaries :P
Browse files Browse the repository at this point in the history
  • Loading branch information
Secre-C committed Nov 12, 2023
1 parent 25a4354 commit 1c9fc7b
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 252 deletions.
74 changes: 54 additions & 20 deletions Emulator/SPD.File.Emulator/Spd/SpdBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using FileEmulationFramework.Lib.IO.Struct;
using FileEmulationFramework.Lib.Utilities;
using Reloaded.Memory.Extensions;
using SPD.File.Emulator.Spr;
using SPD.File.Emulator.Sprite;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -40,28 +41,29 @@ public override unsafe MultiStream Build(IntPtr handle, string filepath, Logger?
var spdFileSlice = new FileSlice(filepath);
var spdStream = new FileSliceStreamW32(spdFileSlice);
_spdHeader = GetHeaderFromSpr<SpdHeader>(spdStream, baseOffset);
_textureEntries = GetTextureEntriesFromFile(spdStream, baseOffset);
_spriteEntries = GetSpriteEntriesFromFile(spdStream, baseOffset);
_textureData = GetTextureDataFromFile(spdFileSlice, baseOffset);
_textureEntries = GetTextureEntriesFromFile(spdStream);
_spriteEntries = GetSpriteEntriesFromFile(spdStream);
_textureData = GetTextureDataFromFile(spdFileSlice);
spdStream.Dispose();

// Write custom sprite entries from '.spdspr' files to sprite dictionary
foreach ( var file in CustomSprFiles.Values )
{
using var stream = new FileSliceStreamFs(file);
using var stream = new FileSliceStreamW32(file);
stream.TryRead(out int spriteId, out _);
stream.Seek(0, SeekOrigin.Begin);

_newSpriteEntries[spriteId] = stream.Read<SpdSpriteEntry>();
}

// Get highest id texture entry
int maxId = _textureEntries.Select(x => x.Key).Max();
int nextId = maxId + 1;
int nextId = _textureEntries.Select(x => x.Key).Max() + 1;

// Create empty HashSet to use for texture names with no exclude separator '~' to reduce allocations
HashSet<int> emptyHashSet = new();

var TextureSeparatedSpriteDict = CreateTextureSeparatedSpriteDict();

// Get DDS filenames and adjust edited sprite texture ids
foreach ( var (key, file) in CustomTextureFiles )
{
Expand Down Expand Up @@ -92,17 +94,21 @@ public override unsafe MultiStream Build(IntPtr handle, string filepath, Logger?
if (!int.TryParse(ids[0], out int texId)) continue;

// Get sprite ids to preserve
HashSet<int> excludeIds = emptyHashSet;
HashSet<int> excludeIds;
if (ids.Length > 1)
excludeIds = GetSpriteIdsFromFilename(ids[1]);
else
excludeIds = emptyHashSet;

// Revert each modified sprite that used to point to the texture, then patch them to point to the new one
foreach (var (spriteId, sprite) in _spriteEntries)
// Revert each modified sprite that used to point to the textures then patch them to point to the new one
if (TextureSeparatedSpriteDict.TryGetValue(texId, out var sprites))
{
if (sprite.GetSpriteTextureId() == texId && !excludeIds.Contains(spriteId))
foreach (var (index, sprite) in sprites)
{
_newSpriteEntries[spriteId] = sprite;
PatchSpriteEntry(spriteId, newId);
if (excludeIds.Contains(index)) continue;

_newSpriteEntries[index] = sprite;
PatchSpriteEntry(index, newId);
}
}
}
Expand Down Expand Up @@ -134,11 +140,11 @@ public override unsafe MultiStream Build(IntPtr handle, string filepath, Logger?
// Calculate filesize
long newFileSize = totalTextureSize + headerLength + textureEntryStream.Length + spriteStream.Length;

_spdHeader._fileSize = (int)newFileSize;
_spdHeader._textureEntryCount = (short)_textureEntries.Count;
_spdHeader._spriteEntryCount = (short)_spriteEntries.Count;
_spdHeader._textureEntryOffset = headerLength;
_spdHeader._spriteEntryOffset = headerLength + (_textureEntries.Count * textureEntryLength);
_spdHeader.fileSize = (int)newFileSize;
_spdHeader.textureEntryCount = (short)_textureEntries.Count;
_spdHeader.spriteEntryCount = (short)_spriteEntries.Count;
_spdHeader.textureEntryOffset = headerLength;
_spdHeader.spriteEntryOffset = headerLength + (_textureEntries.Count * textureEntryLength);

headerStream.Write(_spdHeader);

Expand Down Expand Up @@ -177,7 +183,7 @@ private void PatchSpriteEntry(int spriteId, int newTextureId)
CollectionsMarshal.GetValueRefOrNullRef(_newSpriteEntries, spriteId).SetTextureId(newTextureId);
}

private Dictionary<int, SpdTextureEntry> GetTextureEntriesFromFile(Stream stream, long pos)
private Dictionary<int, SpdTextureEntry> GetTextureEntriesFromFile(Stream stream)
{
Dictionary<int, SpdTextureEntry> textureDictionary = new();

Expand All @@ -194,7 +200,7 @@ private void PatchSpriteEntry(int spriteId, int newTextureId)
return textureDictionary;
}

private Dictionary<int, SpdSpriteEntry> GetSpriteEntriesFromFile(Stream stream, long pos)
private Dictionary<int, SpdSpriteEntry> GetSpriteEntriesFromFile(Stream stream)
{
Dictionary<int, SpdSpriteEntry> spriteDictionary = new();

Expand All @@ -211,7 +217,7 @@ private void PatchSpriteEntry(int spriteId, int newTextureId)
return spriteDictionary;
}

private Dictionary<int, Stream> GetTextureDataFromFile(FileSlice spdSlice, long pos)
private Dictionary<int, Stream> GetTextureDataFromFile(FileSlice spdSlice)
{
// Create a dictionary to hold texture data, with the key being the texture entry's id
Dictionary<int, Stream> textureDataDictionary = new();
Expand Down Expand Up @@ -321,4 +327,32 @@ public static SpdTextureEntry CreateTextureEntry(FileSlice texture, int id)

return entry;
}

/// <summary>
/// Returns a dictionary with the sprite ids separated by texture id.
/// </summary>
private Dictionary<int, Dictionary<int, SpdSpriteEntry>> CreateTextureSeparatedSpriteDict()
{
var resultDict = new Dictionary<int, Dictionary<int, SpdSpriteEntry>>();

foreach (var (id, sprite) in _spriteEntries)
{

int textureId = sprite.GetSpriteTextureId();

if (resultDict.TryGetValue(textureId, out var sprites))
{
sprites[id] = sprite;
}
else
{
resultDict[id] = new Dictionary<int, SpdSpriteEntry>
{
{ id, sprite }
};
}
}

return resultDict;
}
}
14 changes: 7 additions & 7 deletions Emulator/SPD.File.Emulator/Spd/SpdHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ public struct SpdHeader
#pragma warning disable IDE0051 // Remove unused private members
int magic;
int _unk04;
internal int _fileSize;
internal int fileSize;
int _unk0c;
int _unk10;
internal short _textureEntryCount;
internal short _spriteEntryCount;
internal int _textureEntryOffset;
internal int _spriteEntryOffset;
internal short textureEntryCount;
internal short spriteEntryCount;
internal int textureEntryOffset;
internal int spriteEntryOffset;

public readonly (short, int) GetTextureEntryCountAndOffset() => (_textureEntryCount, _textureEntryOffset);
public readonly (short, int) GetSpriteEntryCountAndOffset() => (_spriteEntryCount, _spriteEntryOffset);
public readonly (short, int) GetTextureEntryCountAndOffset() => (textureEntryCount, textureEntryOffset);
public readonly (short, int) GetSpriteEntryCountAndOffset() => (spriteEntryCount, spriteEntryOffset);
#pragma warning restore IDE0044 // Add readonly modifier
#pragma warning restore IDE0051 // Remove unused private members
}
Loading

0 comments on commit 1c9fc7b

Please sign in to comment.