Ejemplo n.º 1
0
        public void CanSaveAndLoadNamesAndFormats()
        {
            var buffer   = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
            var model    = new PokemonModel(buffer);
            var viewPort = new ViewPort("file.txt", model)
            {
                Width = 0x10, Height = 0x10
            };
            StoredMetadata metadata   = null;
            var            fileSystem = new StubFileSystem {
                Save = file => true, SaveMetadata = (file, md) => { metadata = new StoredMetadata(md); return(true); }
            };


            viewPort.Edit("^bob\"\" \"Hello\"");
            viewPort.Save.Execute(fileSystem);

            var model2    = new PokemonModel(buffer, metadata);
            var viewPort2 = new ViewPort("file.txt", model)
            {
                Width = 0x10, Height = 0x10
            };

            Assert.Equal("bob", ((Anchor)viewPort2[0, 0].Format).Name);
        }
Ejemplo n.º 2
0
    public UploadedFile(FileIdentifier id, IFileInfo fileInfo, StoredMetadata metadata)
    {
        this._fileInfo = fileInfo;

        this.Metadata = metadata;
        this.Id       = id;
    }
Ejemplo n.º 3
0
        public void MetadataCanLoadList()
        {
            var lines = new List <string>();

            lines.Add("[[List]]");
            lines.Add("Name = '''moveeffects'''");
            lines.Add("1 = [");
            lines.Add("   '''abc''',");
            lines.Add("   '''\"def\"''',");
            lines.Add("]");
            lines.Add("5 = ['''xyz''']");
            lines.Add("7 = '''bob'''");

            var metadata = new StoredMetadata(lines.ToArray());

            var moveEffects = metadata.Lists.Single(list => list.Name == "moveeffects");

            Assert.Equal(8, moveEffects.Count);
            Assert.Equal("0", moveEffects[0]);
            Assert.Equal("abc", moveEffects[1]);
            Assert.Equal("\"def\"", moveEffects[2]);
            Assert.Equal("3", moveEffects[3]);
            Assert.Equal("4", moveEffects[4]);
            Assert.Equal("xyz", moveEffects[5]);
            Assert.Equal("6", moveEffects[6]);
            Assert.Equal("bob", moveEffects[7]);
        }
Ejemplo n.º 4
0
    private async Task ReserveUploadAsync(UploadLinkModel model)
    {
        CancellationToken cancellationToken = this.HttpContext.RequestAborted;
        StoredMetadata    metadata          = new StoredMetadata {
            Expiration    = model.Expiration,
            UploadedOn    = DateTime.UtcNow,
            IsReservation = true,
            Sender        = model.Sender
        };

        // Ensure no empty contact information
        if (model.Sender != null && String.IsNullOrEmpty(model.Sender.Name) && String.IsNullOrEmpty(model.Sender.EmailAddress))
        {
            metadata.Sender = null;
        }

        // Write away
        using (Stream fileStream = this._fileWriter.OpenWriteStream(this._fileStore.GetMetadataFile(model.FileIdentifier))) {
            using (StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8)) {
                await sw.WriteAsync(metadata.Serialize()).ConfigureAwait(false);

                await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            }
        }
    }
Ejemplo n.º 5
0
        public void OffsetPointerOutOfRange_LoadMetadata_NoOffsetPointer()
        {
            var metadata = new StoredMetadata(offsetPointers: new[] { new StoredOffsetPointer(0x300, 1) });

            Model.LoadMetadata(metadata);

            Assert.Equal(int.MaxValue, Model.GetNextRun(0).Start);
        }
Ejemplo n.º 6
0
        public void OffsetPointerOutOfRange_InitMetadata_NoOffsetPointer()
        {
            var metadata = new StoredMetadata(offsetPointers: new[] { new StoredOffsetPointer(0x300, 1) });

            var model = New.PokemonModel(Data, metadata, Singletons);

            Assert.Equal(int.MaxValue, model.GetNextRun(0).Start);
        }
Ejemplo n.º 7
0
        private static AutoSearchModel LoadModelNoCache(string name)
        {
            Skip.IfNot(File.Exists(name));
            var data     = File.ReadAllBytes(name);
            var metadata = new StoredMetadata(new string[0]);
            var model    = new AutoSearchModel(data, metadata);

            return(model);
        }
Ejemplo n.º 8
0
        public void CanLoadVersionFromToml()
        {
            var file     = @"
[General]
ApplicationVersion = '''0.1.0'''
";
            var metadata = new StoredMetadata(file.Split(Environment.NewLine));

            Assert.Equal("0.1.0", metadata.Version);
        }
Ejemplo n.º 9
0
        public void UnmappedPointerInMetadata_WriteAnchor_ModelUpdates()
        {
            var pointers = new[] { new StoredUnmappedPointer(0x20, "name") };
            var metadata = new StoredMetadata(unmappedPointers: pointers);

            var model = new PokemonModel(new byte[0x200], metadata, Singletons);

            model.ObserveAnchorWritten(Token, "name", new NoInfoRun(0x80));

            Assert.Equal(0x80, model.ReadPointer(0x20));
        }
Ejemplo n.º 10
0
        public void Metadata_Export_ContainsTableGroups()
        {
            var initialMetadata = new StoredMetadata(tableGroups: new[] { new TableGroup("group1", new[] { "table1" }) });

            Model.Load(new byte[0x200], initialMetadata);

            var newMetadata = Model.ExportMetadata(Singletons.MetadataInfo);
            var text        = newMetadata.Serialize();

            Assert.Contains("[[TableGroup]]", text);
        }
Ejemplo n.º 11
0
        public void UnmappedPointerInMetadata_Load_NullPointer()
        {
            var pointers = new [] { new StoredUnmappedPointer(0x20, "name") };
            var metadata = new StoredMetadata(unmappedPointers: pointers);

            var model = new PokemonModel(new byte[0x200], metadata, Singletons);

            var run = model.GetNextRun(0x20);

            Assert.IsType <PointerRun>(run);
            Assert.Equal(0x20, run.Start);
        }
Ejemplo n.º 12
0
        public void DefaultList_ChangeContent_HashDoesNotMatch()
        {
            var text = new List <string>();

            BaseModel.GetDefaultMetadatas().First().Lists[0].AppendContents(text);

            var hashLine = text.Single(line => line.Trim().StartsWith("DefaultHash = '''"));

            text[text.IndexOf(hashLine)] = "DefaultHash = '''0'''";
            var metadata = new StoredMetadata(text.ToArray());

            Assert.False(metadata.Lists[0].HashMatches);
        }
Ejemplo n.º 13
0
        public void NoGroups_UpdateVersion_Groups()
        {
            SetGameCode(HardcodeTablesModel.FireRed);
            var metadata = new StoredMetadata(
                anchors: new[] { new StoredAnchor(0x100, "anchor", string.Empty) },
                generalInfo: New.EarliestVersionInfo
                );

            var model = New.PokemonModel(Model.RawData, metadata, Singletons);

            var groups = model.ExportMetadata(Singletons.MetadataInfo).TableGroups;

            Assert.NotEmpty(groups);
        }
Ejemplo n.º 14
0
        public void NewMetadata_Reopen_MetadataMatches(string romName)
        {
            Skip.IfNot(File.Exists(romName), "Test cannot run without a real rom");

            var singletons = BaseViewModelTestClass.Singletons;
            var fileData   = File.ReadAllBytes(romName);
            var code       = fileData.GetGameCode();

            var freshModel     = new HardcodeTablesModel(singletons, fileData);
            var metadata1      = freshModel.ExportMetadata(singletons.MetadataInfo);
            var serializedBack = new StoredMetadata(metadata1.Serialize());
            var reopenModel    = new HardcodeTablesModel(singletons, fileData, serializedBack);
            var metadata2      = reopenModel.ExportMetadata(singletons.MetadataInfo);

            // verify that the content is the same
            Assert.Equal(metadata1.FreeSpaceBuffer, metadata2.FreeSpaceBuffer);
            // FreeSpaceSearch is allowed to be different
            Assert.Equal(metadata1.Version, metadata2.Version);
            Assert.Equal(metadata1.NextExportID, metadata2.NextExportID);

            foreach (var list in metadata2.Lists)
            {
                var originalList = metadata1.Lists.Single(freshList => freshList.Name == list.Name);
                Assert.Equal(originalList.Count, list.Count);
                Assert.All(originalList.Contents.Count.Range(), i => Assert.Equal(originalList.Contents[i], list.Contents[i]));
            }

            foreach (var matchedWord in metadata2.MatchedWords)
            {
                var originalMatchedWord = metadata1.MatchedWords.Single(freshMatchedWord => freshMatchedWord.Address == matchedWord.Address);
                Assert.Equal(originalMatchedWord.Name, matchedWord.Name);
                Assert.Equal(originalMatchedWord.AddOffset, matchedWord.AddOffset);
                Assert.Equal(originalMatchedWord.MultOffset, matchedWord.MultOffset);
                Assert.Equal(originalMatchedWord.Note, matchedWord.Note);
            }

            foreach (var namedAnchor in metadata2.NamedAnchors)
            {
                var originalNamedAnchor = metadata1.NamedAnchors.Single(anchor => anchor.Name == namedAnchor.Name);
                Assert.Equal(originalNamedAnchor.Address, namedAnchor.Address);
                Assert.Equal(originalNamedAnchor.Format, namedAnchor.Format);
            }

            foreach (var offsetPointer in metadata2.OffsetPointers)
            {
                var originalOffsetPointer = metadata1.OffsetPointers.Single(pointer => pointer.Address == offsetPointer.Address);
                Assert.Equal(originalOffsetPointer.Offset, offsetPointer.Offset);
            }
        }
Ejemplo n.º 15
0
        public void Text_LoadMetadata_ContainsTableGroups()
        {
            var metadata = new StoredMetadata(new[] {
                "[[TableGroup]]",
                "Name = '''group1'''",
                "0 = [",
                "   '''table1''',",
                "   '''table2''',",
                "]",
            });

            Assert.Single(metadata.TableGroups);
            Assert.Equal("group1", metadata.TableGroups[0].GroupName);
            Assert.Equal(new[] { "table1", "table2" }, metadata.TableGroups[0].Tables);
        }
Ejemplo n.º 16
0
        public void Text_LoadMetadata_CaptureHash()
        {
            var text = @"
[[TableGroup]]
Name = '''custom'''
DefaultHash = '''0'''
0 = [
   '''table''',
]
";

            var metadata = new StoredMetadata(text.SplitLines());

            Assert.Equal("0", metadata.TableGroups.Single().Hash);
        }
Ejemplo n.º 17
0
        public void TableGroupWithEditedHash_UpdateVersion_DoNotAddGroupsWithSameTables()
        {
            SetGameCode(HardcodeTablesModel.FireRed);
            var metadata = new StoredMetadata(
                tableGroups: new[] { new TableGroup("custom", new[] { HardcodeTablesModel.PokemonNameTable }, "0") },
                generalInfo: New.EarliestVersionInfo
                );

            var model  = New.PokemonModel(Model.RawData, metadata, Singletons);
            var groups = model.ExportMetadata(Singletons.MetadataInfo).TableGroups;

            var group = groups.Single(group => group.GroupName == "custom");

            Assert.Single(group.Tables);
            Assert.Single(groups.Where(group => group.Tables.Contains(HardcodeTablesModel.PokemonNameTable)));
        }
Ejemplo n.º 18
0
        public void CanSaveAndLoadNamesAndFormats()
        {
            SetFullModel(0xFF);
            var            viewPort   = ViewPort;
            StoredMetadata metadata   = null;
            var            fileSystem = new StubFileSystem {
                Save = file => true, SaveMetadata = (file, md) => { metadata = new StoredMetadata(md); return(true); }
            };

            viewPort.Edit("^bob\"\" \"Hello\"");
            viewPort.Save.Execute(fileSystem);

            var model2    = new PokemonModel(Model.RawData, metadata);
            var viewPort2 = AutoSearchTests.NewViewPort("file.txt", model2);

            Assert.Equal("bob", ((Anchor)viewPort2[0, 0].Format).Name);
        }
Ejemplo n.º 19
0
    public async Task <StoredMetadata?> GetMetadataAsync(IFileInfo file)
    {
        StoredMetadata metadata;

        try {
            string json;
            using (StreamReader sw = new StreamReader(file.CreateReadStream())) {
                json = await sw.ReadToEndAsync().ConfigureAwait(false);
            }

            metadata = StoredMetadata.Deserialize(json);
        } catch (Exception ex) {
            this._logger.LogError(LogEvents.UploadCorrupted, ex, "Metadata of upload at location {0} is corrupted", file.PhysicalPath);
            return(null);
        }

        return(metadata);
    }
Ejemplo n.º 20
0
        public void CanSaveLooseWordRuns()
        {
            var fileSystem = new StubFileSystem();

            string[] metadata = null;
            fileSystem.Save         = file => true;
            fileSystem.SaveMetadata = (file, lines) => { metadata = lines; return(true); };
            StandardSetup(out var data, out var model, out var viewPort);

            viewPort.Edit("::test ");
            viewPort.Save.Execute(fileSystem);

            var storedMetadata = new StoredMetadata(metadata);
            var matchedWord    = storedMetadata.MatchedWords.First();

            Assert.Equal(0, matchedWord.Address);
            Assert.Equal("test", matchedWord.Name);
        }
Ejemplo n.º 21
0
        public void HashDoesNotMatch_UpdateVersion_ListDoesNotUpdate()
        {
            var someRealList = BaseModel.GetDefaultMetadatas().First().Lists.First();

            SetGameCode("BPRE0");

            var content  = new[] { "some", "content" };
            var metadata = new StoredMetadata(
                generalInfo: new StubMetadataInfo {
                VersionNumber = "0.0.1"
            },
                lists: new[] { new StoredList(someRealList.Name, content, hash: "0") }
                );
            var model = new PokemonModel(Model.RawData, metadata, Singletons);

            model.TryGetList(someRealList.Name, out var list);
            Assert.Equal(content, list.ToList());
        }
Ejemplo n.º 22
0
 private static void CheckIsNewerVersionAvailable(EditorViewModel viewModel)
 {
     if (DateTime.Now < viewModel.LastUpdateCheck + TimeSpan.FromDays(1))
     {
         return;
     }
     viewModel.LastUpdateCheck = DateTime.Now;
     try {
         using (var client = new HttpClient()) {
             string content           = client.GetStringAsync(ReleaseUrl).Result;
             var    mostRecentVersion = content
                                        .Split('\n')
                                        .Where(line => line.Contains("/haven1433/HexManiacAdvance/tree/"))
                                        .Select(line => line.Split("title=").Last().Split('"')[1].Split('v').Last())
                                        .First();
             viewModel.IsNewVersionAvailable = StoredMetadata.NeedVersionUpdate(viewModel.Singletons.MetadataInfo.VersionNumber, mostRecentVersion);
         }
     } catch {
         // Exceptions are expected on Windows 7.
         // If anything goes wrong, we probably don't care. It just means that the IsNewVersionAvailable will be false.
     }
 }
Ejemplo n.º 23
0
    private async Task StoreMetadataAsync(FileIdentifier id, StoredMetadata metadata, CancellationToken cancellationToken)
    {
        IFileInfo metadataFile = this._fileStore.GetMetadataFile(id);

        if (metadata.IsReservation)
        {
            StoredMetadata?originalStoredMetadata = await this._metadataReader.GetMetadataAsync(metadataFile);

            if (originalStoredMetadata == null)
            {
                this._logger.LogWarning(LogEvents.UploadFailed, "{0}: Metadata file expected for reservation at {1}", id, metadataFile.PhysicalPath);

                throw new UploadFailedException("Missing metadata for reserved upload");
            }

            // Sync critical metadata
            metadata.Expiration = originalStoredMetadata.Expiration;
            metadata.Sender     = originalStoredMetadata.Sender;

            // Delete metadata so it can be recreated again
            this._fileWriter.Delete(metadataFile);
        }
        else
        {
            // Correct the timestamp with the upload time
            TimeSpan diff = DateTime.UtcNow - this.GetProgressObject(id).StartTime;
            metadata.Expiration += diff;
        }

        // Write away
        using (Stream fileStream = this._fileWriter.OpenWriteStream(metadataFile)) {
            using (StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8)) {
                await sw.WriteAsync(metadata.Serialize()).ConfigureAwait(false);

                await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            }
        }
    }
Ejemplo n.º 24
0
        public void Groups_UpdateVersion_NoDuplicates()
        {
            // calculate 'default' TableGroups
            SetGameCode(HardcodeTablesModel.FireRed);
            var model       = New.HardcodeTablesModel(Singletons, Model.RawData);
            var metadata    = model.ExportMetadata(Singletons.MetadataInfo);
            var tableGroups = metadata.TableGroups;

            // upgrade version
            metadata = new StoredMetadata(
                tableGroups: tableGroups,
                generalInfo: New.EarliestVersionInfo);
            model = New.PokemonModel(model.RawData, metadata, Singletons);

            var newGroups = model.ExportMetadata(Singletons.MetadataInfo).TableGroups;

            Assert.Equal(tableGroups.Count, newGroups.Count);
            for (int i = 0; i < newGroups.Count; i++)
            {
                Assert.Equal(tableGroups[i].GroupName, newGroups[i].GroupName);
                Assert.Equal(tableGroups[i].Hash, newGroups[i].Hash);
            }
        }
Ejemplo n.º 25
0
 public Task DeleteAsync(string name, string config, string credentialsJson)
 {
     StoredMetadata.Remove(name);
     return(Task.CompletedTask);
 }
Ejemplo n.º 26
0
        public void OldMetadata_Upgrade_MetadataMatchesNewLoad(string romName, string tomlName)
        {
            Skip.IfNot(File.Exists(romName), "Test cannot run without a real rom");

            var singletons = BaseViewModelTestClass.Singletons;
            var tomlPath   = "../../../../src/HexManiac.Tests/toml_versions/";
            var fileData   = File.ReadAllBytes(romName);
            var code       = fileData.GetGameCode();

            tomlPath += code + tomlName;
            var oldMetadata = new StoredMetadata(File.ReadAllLines(tomlPath));

            var upgradedModel    = new HardcodeTablesModel(singletons, fileData, oldMetadata);
            var freshModel       = new HardcodeTablesModel(singletons, fileData, new StoredMetadata(new string[0]));
            var upgradedMetadata = upgradedModel.ExportMetadata(singletons.MetadataInfo);
            var freshMetadata    = freshModel.ExportMetadata(singletons.MetadataInfo);

            // verify that the content is the same
            Assert.Equal(upgradedMetadata.FreeSpaceBuffer, freshMetadata.FreeSpaceBuffer);
            // FreeSpaceSearch is allowed to be different
            Assert.Equal(upgradedMetadata.Version, freshMetadata.Version);
            Assert.Equal(upgradedMetadata.NextExportID, freshMetadata.NextExportID);

            // there may be some old non-deleted lists in upgradedMetadata. That's ok.
            // but for every list in the fresh metadata, make sure the upgraded metadata has it.
            foreach (var list in freshMetadata.Lists)
            {
                var updateList = upgradedMetadata.Lists.Single(updatedList => updatedList.Name == list.Name);
                Assert.Equal(list.Count, updateList.Count);
                Assert.All(updateList.Contents.Count.Range(), i => Assert.Equal(list.Contents[i], updateList.Contents[i]));
            }

            foreach (var matchedWord in upgradedMetadata.MatchedWords)
            {
                var newMatchedWord = freshMetadata.MatchedWords.Single(freshMatchedWord => freshMatchedWord.Address == matchedWord.Address);
                Assert.Equal(newMatchedWord.Name, matchedWord.Name);
                Assert.Equal(newMatchedWord.AddOffset, matchedWord.AddOffset);
                Assert.Equal(newMatchedWord.MultOffset, matchedWord.MultOffset);
                Assert.Equal(newMatchedWord.Note, matchedWord.Note);
            }

            // every anchor in the fresh metadata should be represented in the upgrade case
            var upgradeAnchorNames = upgradedMetadata.NamedAnchors.Select(na => na.Name).ToList();

            foreach (var namedAnchor in freshMetadata.NamedAnchors)
            {
                Assert.Contains(namedAnchor.Name, upgradeAnchorNames);
            }
            // every anchor in the upgraded metadata should have the right address and format to match the fresh versions
            foreach (var namedAnchor in upgradedMetadata.NamedAnchors)
            {
                var newNamedAnchor = freshMetadata.NamedAnchors.Single(anchor => anchor.Name == namedAnchor.Name);
                Assert.Equal(newNamedAnchor.Address, namedAnchor.Address);
                Assert.Equal(newNamedAnchor.Format, namedAnchor.Format);
            }

            foreach (var offsetPointer in upgradedMetadata.OffsetPointers)
            {
                var newOffsetPointer = freshMetadata.OffsetPointers.Single(pointer => pointer.Address == offsetPointer.Address);
                Assert.Equal(newOffsetPointer.Offset, offsetPointer.Offset);
            }
        }
Ejemplo n.º 27
0
 public static IDataModel PokemonModel(byte[] data, StoredMetadata metadata, Singletons singletons) => new PokemonModel(data, metadata, singletons);
Ejemplo n.º 28
0
 public static IDataModel HardcodeTablesModel(Singletons singletons, byte[] rawData, StoredMetadata metadata = null) => new HardcodeTablesModel(singletons, rawData, metadata);
Ejemplo n.º 29
0
        public void OldMetadata_Upgrade_MetadataMatchesNewLoad(string romName, string tomlName)
        {
            Skip.IfNot(File.Exists(romName), "Test cannot run without a real rom");

            var singletons = BaseViewModelTestClass.Singletons;
            var tomlPath   = "../../../../../src/HexManiac.Tests/toml_versions/";
            var fileData   = File.ReadAllBytes(romName);
            var code       = fileData.GetGameCode();

            tomlPath += code + tomlName;
            var oldMetadata = new StoredMetadata(File.ReadAllLines(tomlPath));

            var upgradedModel    = new HardcodeTablesModel(singletons, fileData, oldMetadata);
            var freshModel       = new HardcodeTablesModel(singletons, fileData, new StoredMetadata(new string[0]));
            var upgradedMetadata = upgradedModel.ExportMetadata(singletons.MetadataInfo);
            var freshMetadata    = freshModel.ExportMetadata(singletons.MetadataInfo);

            // verify that the content is the same
            Assert.Equal(upgradedMetadata.FreeSpaceBuffer, freshMetadata.FreeSpaceBuffer);
            // FreeSpaceSearch is allowed to be different
            Assert.Equal(upgradedMetadata.Version, freshMetadata.Version);
            Assert.Equal(upgradedMetadata.NextExportID, freshMetadata.NextExportID);

            // there may be some old non-deleted lists in upgradedMetadata. That's ok.
            // but for every list in the fresh metadata, make sure the upgraded metadata has it.
            foreach (var list in freshMetadata.Lists)
            {
                var updateList = upgradedMetadata.Lists.Single(updatedList => updatedList.Name == list.Name);
                Assert.Equal(list.Count, updateList.Count);
                Assert.All(updateList.Contents.Count.Range(), i => Assert.Equal(list.Contents[i], updateList.Contents[i]));
            }

            foreach (var matchedWord in upgradedMetadata.MatchedWords)
            {
                var newMatchedWord = freshMetadata.MatchedWords.Single(freshMatchedWord => freshMatchedWord.Address == matchedWord.Address);
                Assert.Equal(newMatchedWord.Name, matchedWord.Name);
                Assert.Equal(newMatchedWord.AddOffset, matchedWord.AddOffset);
                Assert.Equal(newMatchedWord.MultOffset, matchedWord.MultOffset);
                Assert.Equal(newMatchedWord.Note, matchedWord.Note);
            }

            // every anchor in the fresh metadata should be represented in the upgrade case
            var upgradeAnchorNames = upgradedMetadata.NamedAnchors.Select(na => na.Name).ToList();

            foreach (var namedAnchor in freshMetadata.NamedAnchors)
            {
                Assert.Contains(namedAnchor.Name, upgradeAnchorNames);
            }
            // every anchor in the upgraded metadata should have the right address and format to match the fresh versions
            foreach (var namedAnchor in upgradedMetadata.NamedAnchors)
            {
                bool exemptAddress = false, exemptFormat = false;
                if (tomlName == "_0.4.0.toml")
                {
                    exemptAddress |= new[] { // legitimate moves: same name, new location
                        "graphics.gamecorner.game.palette",
                        "graphics.bag.inside2.palette",
                    }.Contains(namedAnchor.Name);
                    exemptFormat |= new[] { // legitimate format changes: same name, new format
                        "graphics.gamecorner.game.palette",
                        "scripts.specials.thumb",
                    }.Contains(namedAnchor.Name);
                }
                if (tomlName == "_0.3.0.toml" || tomlName == "_0.3.5.toml")
                {
                    exemptFormat |= new[] { // legitimate format changes: same name, new format
                        "scripts.specials.thumb",
                    }.Contains(namedAnchor.Name);
                }

                var newNamedAnchor = freshMetadata.NamedAnchors.Single(anchor => anchor.Name == namedAnchor.Name);
                if (!exemptAddress)
                {
                    Assert.True(newNamedAnchor.Address == namedAnchor.Address, $"Did {namedAnchor.Name} move?");
                }
                if (!exemptFormat)
                {
                    Assert.True(newNamedAnchor.Format == namedAnchor.Format, $"Did {namedAnchor.Name} get a new format? {newNamedAnchor.Format} (new) != {namedAnchor.Format} (old)");
                }
            }

            foreach (var offsetPointer in upgradedMetadata.OffsetPointers)
            {
                var newOffsetPointer = freshMetadata.OffsetPointers.Single(pointer => pointer.Address == offsetPointer.Address);
                Assert.Equal(newOffsetPointer.Offset, offsetPointer.Offset);
            }
        }