feat(replay): add quirks codec and ROM library
This commit is contained in:
1 parent
ae9f623b49
commit
a312d7e440
2 files changed
+175
No files matched your search
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Chip8.Core;
|
||||||
|
|
||||||
|
namespace Chip8.Replay;
|
||||||
|
|
||||||
|
public static class QuirksCodec
|
||||||
|
{
|
||||||
|
const int shiftUsesVyBit = 0;
|
||||||
|
const int jumpWithVxBit = 1;
|
||||||
|
const int loadStoreIncrementsIBit = 2;
|
||||||
|
const int resetVfOnLogicBit = 3;
|
||||||
|
const int clipSpritesBit = 4;
|
||||||
|
const int addToIndexSetsCarryBit = 5;
|
||||||
|
|
||||||
|
static readonly (string token, int bit)[] tokens =
|
||||||
|
{
|
||||||
|
("shiftVy", shiftUsesVyBit),
|
||||||
|
("jumpVx", jumpWithVxBit),
|
||||||
|
("incI", loadStoreIncrementsIBit),
|
||||||
|
("resetVf", resetVfOnLogicBit),
|
||||||
|
("clip", clipSpritesBit),
|
||||||
|
("carryI", addToIndexSetsCarryBit),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static byte toByte(Quirks quirks)
|
||||||
|
{
|
||||||
|
int packed = 0;
|
||||||
|
if (quirks.shiftUsesVy) packed |= 1 << shiftUsesVyBit;
|
||||||
|
if (quirks.jumpWithVx) packed |= 1 << jumpWithVxBit;
|
||||||
|
if (quirks.loadStoreIncrementsI) packed |= 1 << loadStoreIncrementsIBit;
|
||||||
|
if (quirks.resetVfOnLogic) packed |= 1 << resetVfOnLogicBit;
|
||||||
|
if (quirks.clipSprites) packed |= 1 << clipSpritesBit;
|
||||||
|
if (quirks.addToIndexSetsCarry) packed |= 1 << addToIndexSetsCarryBit;
|
||||||
|
return (byte)packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Quirks fromByte(byte packed) => new Quirks
|
||||||
|
{
|
||||||
|
shiftUsesVy = (packed & (1 << shiftUsesVyBit)) != 0,
|
||||||
|
jumpWithVx = (packed & (1 << jumpWithVxBit)) != 0,
|
||||||
|
loadStoreIncrementsI = (packed & (1 << loadStoreIncrementsIBit)) != 0,
|
||||||
|
resetVfOnLogic = (packed & (1 << resetVfOnLogicBit)) != 0,
|
||||||
|
clipSprites = (packed & (1 << clipSpritesBit)) != 0,
|
||||||
|
addToIndexSetsCarry = (packed & (1 << addToIndexSetsCarryBit)) != 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string toTokens(Quirks quirks)
|
||||||
|
{
|
||||||
|
byte packed = toByte(quirks);
|
||||||
|
var active = new List<string>();
|
||||||
|
foreach (var (token, bit) in tokens)
|
||||||
|
{
|
||||||
|
if ((packed & (1 << bit)) != 0)
|
||||||
|
{
|
||||||
|
active.Add(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return active.Count == 0 ? "none" : string.Join("+", active);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Quirks fromTokens(string text)
|
||||||
|
{
|
||||||
|
byte packed = 0;
|
||||||
|
string trimmed = text.Trim();
|
||||||
|
if (trimmed == "none" || trimmed.Length == 0)
|
||||||
|
{
|
||||||
|
return fromByte(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string chunk in trimmed.Split('+'))
|
||||||
|
{
|
||||||
|
string token = chunk.Trim();
|
||||||
|
if (token.Length == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matched = false;
|
||||||
|
foreach (var (candidate, bit) in tokens)
|
||||||
|
{
|
||||||
|
if (string.Equals(candidate, token, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
packed |= (byte)(1 << bit);
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched)
|
||||||
|
{
|
||||||
|
throw new FormatException("unknown quirk token '" + token + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromByte(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Quirks clone(Quirks quirks) => fromByte(toByte(quirks));
|
||||||
|
|
||||||
|
public static bool sameFlags(Quirks left, Quirks right) => toByte(left) == toByte(right);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Chip8.Replay;
|
||||||
|
|
||||||
|
public sealed class RomLibrary
|
||||||
|
{
|
||||||
|
readonly string romDirectory;
|
||||||
|
readonly Dictionary<string, byte[]> cache = new Dictionary<string, byte[]>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public RomLibrary(string romDirectory)
|
||||||
|
{
|
||||||
|
this.romDirectory = romDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string directory => romDirectory;
|
||||||
|
|
||||||
|
public static RomLibrary locate()
|
||||||
|
{
|
||||||
|
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
||||||
|
while (dir is not null && !Directory.Exists(Path.Combine(dir.FullName, "roms")))
|
||||||
|
{
|
||||||
|
dir = dir.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir is null)
|
||||||
|
{
|
||||||
|
throw new DirectoryNotFoundException("could not locate a 'roms' directory above " + AppContext.BaseDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RomLibrary(Path.Combine(dir.FullName, "roms"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool has(string name) => File.Exists(pathOf(name));
|
||||||
|
|
||||||
|
public string pathOf(string name) => Path.Combine(romDirectory, name);
|
||||||
|
|
||||||
|
public byte[] load(string name)
|
||||||
|
{
|
||||||
|
if (cache.TryGetValue(name, out byte[]? cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
string path = pathOf(name);
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("rom '" + name + "' not found under " + romDirectory, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = File.ReadAllBytes(path);
|
||||||
|
cache[name] = bytes;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<string> available()
|
||||||
|
{
|
||||||
|
var names = new List<string>();
|
||||||
|
if (!Directory.Exists(romDirectory))
|
||||||
|
{
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string path in Directory.GetFiles(romDirectory, "*.ch8"))
|
||||||
|
{
|
||||||
|
names.Add(Path.GetFileName(path));
|
||||||
|
}
|
||||||
|
names.Sort(StringComparer.Ordinal);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user