// precondition: rombyte (minimum of) read lock already acquired private void CompareToExisting(ByteEntry romByte) { MDb = romByte.DataBank != DataBank; MMarks = romByte.TypeFlag != FlagType; MDp = romByte.DirectPage != DirectPage; Mx = romByte.XFlag != XFlagSet; Mm = romByte.MFlag != MFlagSet; Changed = MMarks || MDb || MDp || Mx || Mm; }
private static ByteEntry CreateByteOneAnnotation(Annotation annotation, bool dontSetParentOnCollectionItems = false) { var entry = new ByteEntry(new AnnotationCollection { annotation }) { DontSetParentOnCollectionItems = dontSetParentOnCollectionItems, }; Assert.Single(entry.Annotations); Assert.Equal(dontSetParentOnCollectionItems, dontSetParentOnCollectionItems); return(entry); }
private static ByteEntry CreateByteEntryByFlatteningGraph( ByteGraphNode byteGraphNode, ByteEntry rootEntryToPopulate = null ) { byteGraphNode.Validate(); EnsureRootEntryExists(); PopulateFromChildNodes(); // use child data first PopulateFromRootNode(); // override/append our data second as the priority return(rootEntryToPopulate); void EnsureRootEntryExists() { rootEntryToPopulate ??= new ByteEntry { Parent = byteGraphNode.ByteSource.Bytes, ParentIndex = byteGraphNode.ByteEntry?.ParentIndex ?? byteGraphNode.SourceIndex, // several of our collection items (bytes, annotations, etc) will store // a reference to their parent container. since this new ByteEntry object we're making // is more of an aggregator, we don't want to modify the parents of the things we're storing. // we just store a reference, they really live elsewhere and hence, we're not their 'real' parent. // "YOU'RE NOT MY REAL DAD" DontSetParentOnCollectionItems = true, }; } void PopulateFromChildNodes() { // traverse any child nodes first. if (byteGraphNode.Children == null) { return; } foreach (var childNode in byteGraphNode.Children) { CreateByteEntryByFlatteningGraph(childNode, rootEntryToPopulate); } } void PopulateFromRootNode() { // annotations are concatenated together if (byteGraphNode.ByteEntry?.Annotations?.Count > 0) { rootEntryToPopulate.AppendAnnotationsFrom(byteGraphNode.ByteEntry); } } }
static void TestParents(ByteEntry byteEntry9) { var by1 = byteEntry9.Byte; Assert.NotNull(by1); Assert.Equal(0xCA, by1.Value); // TODO // Assert.NotNull(byteEntry9.ParentByteSource); Assert.Single(byteEntry9.Annotations); foreach (var annotation in byteEntry9.Annotations) { Assert.NotNull(annotation.Parent); Assert.True(ReferenceEquals(byteEntry9, annotation.Parent)); } }
public void ApplyModificationIfNeeded(ByteEntry byteOffset) { byteOffset.Lock.EnterUpgradeableReadLock(); try { CompareToExisting(byteOffset); if (Changed) { ApplyModification(byteOffset); } } finally { byteOffset.Lock.ExitUpgradeableReadLock(); } }
// precondition: ByteEntry (minimum of) read lock already acquired private void ApplyModification(ByteEntry byteOffset) { byteOffset.Lock.EnterWriteLock(); try { byteOffset.TypeFlag = FlagType; byteOffset.DataBank = (byte)DataBank; byteOffset.DirectPage = 0xFFFF & DirectPage; byteOffset.XFlag = XFlagSet; byteOffset.MFlag = MFlagSet; } finally { byteOffset.Lock.ExitWriteLock(); } }
// note: performance-intensive function. be really careful when adding stuff here. public ByteEntry DecodeRomByte(string line) { var input = PrepLine(line); var newByte = new ByteEntry(); var flagTxt = input[0]; var otherFlags1 = Fake64Encoding.DecodeHackyBase64(input[1]); newByte.DataBank = ByteUtil.ByteParseHex2(input[2], input[3]); newByte.DirectPage = (int)ByteUtil.ByteParseHex4(input[4], input[5], input[6], input[7]); newByte.Arch = (Architecture)(ByteUtil.ByteParseHex1(input[8]) & 0x3); #if EXTRA_DEBUG_CHECKS Debug.Assert(Fake64Encoding.EncodeHackyBase64(otherFlags1) == o1_str); #endif newByte.XFlag = ((otherFlags1 >> 2) & 0x1) != 0; newByte.MFlag = ((otherFlags1 >> 3) & 0x1) != 0; newByte.Point = (InOutPoint)((otherFlags1 >> 4) & 0xF); var found = false; foreach (var e in FlagEncodeTable) { if (e.Key != flagTxt) { continue; } newByte.TypeFlag = e.Value; found = true; break; } if (!found) { throw new InvalidDataException("Unknown FlagType"); } return(newByte); }
public static void TestSuccessfulUnrelatedMultipleCombine() { var entry1 = new ByteEntry(new AnnotationCollection { new Label { Name = "test1111" }, new ByteAnnotation(), new Comment() }) { DontSetParentOnCollectionItems = true }; var entry2 = new ByteEntry(new AnnotationCollection { new OpcodeAnnotation(), new ByteAnnotation(), new MarkAnnotation() }); Assert.True(entry1.DontSetParentOnCollectionItems); Assert.Null(Record.Exception(() => entry1.AppendAnnotationsFrom(entry2))); Assert.Equal(5, entry1.Annotations.Count); var expectedItemsCount = new List <Type> { typeof(Label), typeof(ByteAnnotation), typeof(Comment), typeof(OpcodeAnnotation), typeof(MarkAnnotation) }.ToDictionary(item => item, _ => 0); foreach (var item in entry1.Annotations) { var currentTotal = ++expectedItemsCount[item.GetType()]; Assert.True(currentTotal <= 1); } var anyNotOne = expectedItemsCount.Any(pair => pair.Value != 1); Assert.False(anyNotOne); }
public string EncodeByte(ByteEntry instance) { // use a custom formatter here to save space. there are a LOT of ROMBytes. // despite that we're still going for: // 1) text only for slightly human readability // 2) mergability in git/etc // // some of this can be unpacked further to increase readability without // hurting the filesize too much. figure out what's useful. // // sorry, I know the encoding looks insane and weird and specific. this reduced my // save file size from 42MB to less than 13MB // NOTE: must be uppercase letter or "=" or "-" // if you add things here, make sure you understand the compression settings above. var flagTxt = ' '; foreach (var e in FlagEncodeTable) { if (e.Value != instance.TypeFlag) { continue; } flagTxt = e.Key; break; } if (flagTxt == ' ') { throw new InvalidDataException("Unknown FlagType"); } // max 6 bits if we want to fit in 1 base64 ASCII digit var otherFlags1 = (byte)( (instance.XFlag ? 1 : 0) << 2 | // 1 bit (instance.MFlag ? 1 : 0) << 3 | // 1 bit (byte)instance.Point << 4 // 4 bits // LEAVE OFF THE LAST 2 BITS. it'll mess with the base64 below ); // reminder: when decoding, have to cut off all but the first 6 bits var o1Str = Fake64Encoding.EncodeHackyBase64(otherFlags1); Debug.Assert(Fake64Encoding.DecodeHackyBase64(o1Str) == otherFlags1); if (!instance.XFlag && !instance.MFlag && instance.Point == 0) { Debug.Assert(o1Str == '0'); // sanity } // this is basically going to be "0" almost 100% of the time. // we'll put it on the end of the string so it's most likely not output byte otherFlags2 = (byte)( (byte)instance.Arch << 0 // 2 bits ); var o2Str = otherFlags2.ToString("X1"); Debug.Assert(o2Str.Length == 1); // ordering: put DB and D on the end, they're likely to be zero and compressible var sb = new StringBuilder(9); sb.Append(flagTxt); sb.Append(o1Str); sb.Append(instance.DataBank.ToString("X2")); sb.Append(instance.DirectPage.ToString("X4")); sb.Append(o2Str); Debug.Assert(sb.Length == 9); var data = sb.ToString(); // light compression: chop off any trailing zeroes. // this alone saves a giant amount of space. data = data.TrimEnd(new[] { '0' }); // future compression but dilutes readability: // if type is opcode or operand with same flags, combine those into 1 type. // i.e. take an opcode with 3 operands, represent it using one digit in the file. // instead of "=---", we swap with "+" or something. small optimization. return(data); }