// 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;
            }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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);
                }
            }
        }
Ejemplo n.º 4
0
            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();
     }
 }
Ejemplo n.º 7
0
        // 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);
        }
Ejemplo n.º 8
0
        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);
        }
Ejemplo n.º 9
0
        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);
        }