public void AddingToAnArrayWithFixedLengthUpdatesTheAnchorFormat() { // arrange var(model, viewPort) = (Model, ViewPort); var delta = new ModelDelta(); var errors = new List <string>(); var elements = new[] { "123", "alice", "candy land", "hello world", "fortify" }; for (int i = 0; i < elements.Length; i++) { var content = PCSString.Convert(elements[i]); while (content.Count < 0x10) { content.Add(0x00); } Array.Copy(content.ToArray(), 0, model.RawData, 0x10 * i + 0x20, 0x10); } model.WritePointer(delta, 0x00, 0x20); model.ObserveRunWritten(delta, new PointerRun(0x00)); model.WritePointer(delta, 0x04, 0x90); model.ObserveRunWritten(delta, new PointerRun(0x04)); // the anchor at 0x90 should prevent a paste overwrite viewPort.SelectionStart = new Point(0, 2); viewPort.Edit("^testdata[name\"\"16]5 "); viewPort.Goto.Execute("000000"); viewPort.OnError += (sender, message) => errors.Add(message); // act -> add an element viewPort.SelectionStart = new Point(0, 7); viewPort.Edit("+\"crab\""); // assert -> length changed viewPort.SelectionStart = new Point(0, 2); Assert.True(viewPort.AnchorTextVisible); Assert.Equal("^testdata[name\"\"16]6", viewPort.AnchorText); }
public void CanAutoDetectMultipleTextRunsAtOnce() { var test = new BaseViewModelTestClass(); var token = test.ViewPort.CurrentChange; // Arrange some undetected pointers to some undetected text test.Model.WritePointer(token, 0, 0x10); test.Model.WritePointer(token, 4, 0x15); int write = 0x10; test.Model[0xF] = 0xFF; PCSString.Convert("text").ForEach(b => test.Model[write++] = b); PCSString.Convert("more").ForEach(b => test.Model[write++] = b); // add some more text/pointers later on that *are* detected test.Model.WritePointer(token, 8, 0x40); test.Model[0x40] = 0xFF; test.ViewPort.Edit("@40 ^discovered\"\" Blob\" @00 "); // select from partway through the first text to partway through the second text and "Display as Text" test.ViewPort.SelectionStart = new Point(1, 1); test.ViewPort.SelectionEnd = new Point(7, 1); var group = (ContextItemGroup)test.ViewPort.GetContextMenuItems(new Point(4, 1)).Single(item => item.Text == "Display As..."); var button = group.Single(item => item.Text == "Text"); button.Command.Execute(); // Verify that we found both Assert.Equal(0, test.Model.GetNextRun(0).Start); // first pointer Assert.Equal(4, test.Model.GetNextRun(4).Start); // second pointer Assert.Equal(0x10, test.Model.GetNextRun(0x10).Start); // first text Assert.Equal(0x15, test.Model.GetNextRun(0x15).Start); // second text }
public static object BuildContentForRun(IDataModel model, int source, int destination, IFormattedRun destinationRun, int preferredPaletteStart = -1, int preferredSpritePage = 0) { if (destination != destinationRun.Start) { return(null); } if (destinationRun is PCSRun pcs) { return(PCSString.Convert(model, pcs.Start, pcs.Length)); } else if (destinationRun is ISpriteRun sprite) { if (sprite is LzTilemapRun tilemap) { tilemap.FindMatchingTileset(model); } var paletteRuns = sprite.FindRelatedPalettes(model, source); var paletteRun = paletteRuns.FirstOrDefault(); if (preferredPaletteStart >= 0) { paletteRun = paletteRuns.FirstOrDefault(pRun => pRun.Start == preferredPaletteStart) ?? model.GetNextRun(preferredPaletteStart) as IPaletteRun; } var pixels = sprite.GetPixels(model, preferredSpritePage); if (pixels == null) { return(null); } var colors = paletteRun?.AllColors(model) ?? TileViewModel.CreateDefaultPalette((int)Math.Pow(2, sprite.SpriteFormat.BitsPerPixel)); var imageData = SpriteTool.Render(pixels, colors, paletteRun?.PaletteFormat.InitialBlankPages ?? 0, 0); return(new ReadonlyPixelViewModel(sprite.SpriteFormat, imageData)); } else if (destinationRun is IPaletteRun paletteRun) { var colors = paletteRun.GetPalette(model, 0); return(new ReadonlyPaletteCollection(colors)); } else if (destinationRun is IStreamRun streamRun) { using (ModelCacheScope.CreateScope(model)) { var lines = streamRun.SerializeRun().Split(Environment.NewLine); if (lines.Length > 20) { lines = lines.Take(20).ToArray(); } return(Environment.NewLine.Join(lines)); } } else if (destinationRun is ArrayRun arrayRun) { var stream = new StringBuilder(); arrayRun.AppendTo(model, stream, arrayRun.Start, arrayRun.ElementLength * Math.Min(20, arrayRun.ElementCount), false); return(stream.ToString()); } else { return(null); } }
private static void WriteStrings(byte[] buffer, int start, params string[] content) { foreach (var item in content) { var bytes = PCSString.Convert(item).ToArray(); Array.Copy(bytes, 0, buffer, start, bytes.Length); start += bytes.Length; } }
public void TextWithEscape_Search_Find() { var text = "Some\\nText"; PCSString.Convert(text).WriteInto(Model.RawData, 0x20); var results = ViewPort.Find(text); Assert.Equal(0x20, results.Single().start); }
public void Run_Paste00DirectiveWithSameFormat_NoError() { Array.Copy(PCSString.Convert("Hello World").ToArray(), Model.RawData, 12); Model.ObserveRunWritten(new ModelDelta(), new PCSRun(Model, 0, 12)); ViewPort.Refresh(); ViewPort.Edit("@!00(12) ^anchor\"\" "); Assert.Empty(Errors); }
public void Text_TypeDot_DotAdded() { var test = new BaseViewModelTestClass(); Array.Copy(PCSString.Convert("Hello World").ToArray(), test.Model.RawData, 12); test.Model.ObserveRunWritten(test.ViewPort.CurrentChange, new PCSRun(test.Model, 0, 12)); test.ViewPort.Refresh(); test.ViewPort.Edit("@01 ."); Assert.Equal(2, test.ViewPort.ConvertViewPointToAddress(test.ViewPort.SelectionStart)); Assert.Equal("\"H.llo World\"", PCSString.Convert(test.Model, 0, 12)); }
private string SerializeSingleElementStream() { Debug.Assert(endStream is FixedLengthStreamStrategy flss && flss.Count == 1); var result = new StringBuilder(); int offset = Start; var longestLabel = ElementContent.Select(seg => seg.Name.Length).Max(); for (int i = 0; i < ElementContent.Count; i++) { var segment = ElementContent[i]; var rawValue = model.ReadMultiByteValue(offset, segment.Length); var value = rawValue.ToString(); if (segment is ArrayRunEnumSegment enumSeg) { var options = enumSeg.GetOptions(model).ToList(); if (options.Count > rawValue) { value = options[rawValue]; } } else if (segment is ArrayRunTupleSegment tupSeg) { value = tupSeg.ToText(model, offset); } else if (segment is ArrayRunHexSegment hexSeg) { value = "0x" + rawValue.ToString("X" + segment.Length * 2); } else if (segment.Type == ElementContentType.Pointer) { var pointerValue = rawValue - BaseModel.PointerOffset; value = $"<{pointerValue:X6}>"; if (pointerValue == Pointer.NULL) { value = "<null>"; } } else if (segment.Type == ElementContentType.PCS) { value = PCSString.Convert(model, offset, segment.Length); } var extraWhitespace = new string(' ', longestLabel - segment.Name.Length); result.Append($"{segment.Name}:{extraWhitespace} {value}"); if (i < ElementContent.Count - 1) { result.AppendLine(); } offset += segment.Length; } return(result.ToString()); }
public void CanUseViewPortToAutoFindTextWithoutKnowingAboutPointersToIt() { var text = PCSString.Convert("This is some text."); SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); text.CopyTo(model.RawData, 0x10); model.WritePointer(new ModelDelta(), 0x00, 0x10); viewPort.SelectionStart = new Point(3, 1); // just a random byte in the middle of the text viewPort.IsText.Execute(); // this line should find the start of the text and add a run, even with no pointer to it Assert.IsType <PCS>(viewPort[3, 1].Format); }
public void CanFindStringsInData() { var buffer = Enumerable.Repeat((byte)0xFF, 0x100).ToArray(); var data = PCSString.Convert("Hello World!").ToArray(); Array.Copy(data, 0, buffer, 0x10, data.Length); var model1 = new PokemonModel(buffer); var token = new ModelDelta(); model1.WritePointer(token, 0x00, 0x10); var model = new PokemonModel(buffer); Assert.IsType <PCSRun>(model.GetNextRun(0x10)); }
public void CanRecognizeString() { var buffer = new byte[0x100]; var model = new PokemonModel(buffer); var token = new ModelDelta(); var data = PCSString.Convert("Hello World!").ToArray(); Array.Copy(data, 0, buffer, 0x10, data.Length); model.ObserveRunWritten(token, new PCSRun(model, 0x10, data.Length)); var run = (PCSRun)model.GetNextRun(0); Assert.Equal(data.Length, run.Length); }
public void StringWithControlCode_Parsed_CorrectBytes() { var model = new PokemonModel(new byte[0x100]); int i = 0; Write(model, ref i, "ABC"); model[i++] = PCSString.FunctionEscape; model[i++] = 0x09; //pause: no variables Write(model, ref i, "XYZ\""); var target = model.Take(i).ToArray(); var result = PCSString.Convert("ABC\\CC09XYZ").ToArray(); Assert.Equal(target, result); }
public void DisplayAsText_PreviousByteRandom_StillFindTextStartingHere() { PCSString.Convert("Some Content").WriteInto(Model.RawData, 0x21); Model.WritePointer(new ModelDelta(), 3, 0x21); ViewPort.Goto.Execute(0x21); var menu = ViewPort.GetContextMenuItems(ViewPort.SelectionStart); menu = menu.GetSubmenu("Display As..."); var displayText = menu.Single(item => item.Text == "Text"); displayText.Command.Execute(); Assert.IsType <PCSRun>(Model.GetNextRun(0x21)); }
public void EventScriptWithText_ClearText_ScriptIsUnchanged() { ViewPort.Edit("@100!put(FF) ^text\"\" Hello"); var script = @" loadpointer 00 <100> { } end "; var code = ViewPort.Tools.CodeTool.ScriptParser.Compile(ViewPort.CurrentChange, Model, 0, ref script, out var _); Assert.Equal(7, code.Length); Assert.Equal("\"\"", PCSString.Convert(Model, 0x100, 0x100)); }
public void UsingTheAnchorEditorToSetStringFormatChangesVisibleData() { SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); var bytes = PCSString.Convert("Hello World!").ToArray(); Array.Copy(bytes, 0, model.RawData, 0x08, bytes.Length); viewPort.SelectionStart = new Point(0x08, 0); viewPort.Edit("^bob "); viewPort.AnchorText = "^bob\"\""; var anchor = (Anchor)viewPort[8, 0].Format; Assert.IsType <PCS>(anchor.OriginalFormat); }
public void StringSearchAutomaticallySearchesForPointersToResults() { var text = "This is the song that never ends."; var bytes = PCSString.Convert(text).ToArray(); SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); Array.Copy(bytes, 0, model.RawData, 0x32, bytes.Length); // the data itself, positioned at x32 (won't be automatically found on load) Array.Copy(new byte[] { 0x32, 0, 0, 0x08 }, 0, model.RawData, 0x10, 4); // the pointer to the data. Pointer is aligned, but data is not. // the act of searching should find the anchor var results = viewPort.Find("this is the song"); Assert.Single(results); Assert.Single(model.GetNextRun(0x32).PointerSources); }
public override IDataFormat CreateDataFormat(IDataModel data, int index) { var offsets = ConvertByteOffsetToArrayOffset(index); var currentSegment = ElementContent[offsets.SegmentIndex]; if (currentSegment.Type == ElementContentType.PCS) { if (currentCachedStartIndex != offsets.SegmentStart || currentCachedIndex > offsets.SegmentOffset) { currentCachedStartIndex = offsets.SegmentStart; currentCachedIndex = offsets.SegmentOffset; cachedCurrentString = PCSString.Convert(data, offsets.SegmentStart, currentSegment.Length); } return(PCSRun.CreatePCSFormat(data, offsets.SegmentStart, index, cachedCurrentString)); } var position = index - offsets.SegmentStart; if (currentSegment.Type == ElementContentType.Integer) { if (currentSegment is ArrayRunEnumSegment enumSegment) { var value = enumSegment.ToText(data, index); return(new IntegerEnum(offsets.SegmentStart, position, value, currentSegment.Length)); } else { var value = ArrayRunElementSegment.ToInteger(data, offsets.SegmentStart, currentSegment.Length); return(new Integer(offsets.SegmentStart, position, value, currentSegment.Length)); } } if (currentSegment.Type == ElementContentType.Pointer) { var destination = data.ReadPointer(offsets.SegmentStart); var destinationName = data.GetAnchorFromAddress(offsets.SegmentStart, destination); return(new Pointer(offsets.SegmentStart, position, destination, destinationName)); } if (currentSegment.Type == ElementContentType.BitArray) { return(new BitArray(offsets.SegmentStart, position, currentSegment.Length)); } throw new NotImplementedException(); }
public void EventScriptWithText_ExpandTextSoItMoves_ScriptPointerUpdates() { SetFullModel(0xFF); ViewPort.Edit("^script`xse` 0F 00 <100> 02 @100 ^text\"\" \"short\" 07 @00 "); var tool = ViewPort.Tools.CodeTool.Contents[0]; var lines = tool.Content.SplitLines(); // label, loadpointer, curly, text, curly, end lines[3] = "longer"; tool.Content = Script(lines); var destination = Model.ReadPointer(2); Assert.Equal(destination, Model.GetNextRun(destination).Start); Assert.Equal("\"longer\"", PCSString.Convert(Model, destination, 10)); Assert.Equal("text", Model.GetAnchorFromAddress(-1, destination)); }
public void StringSearchAutomaticallySearchesForPointersToResults() { var text = "This is the song that never ends."; var bytes = PCSString.Convert(text).ToArray(); var buffer = new byte[0x200]; Array.Copy(bytes, 0, buffer, 0x32, bytes.Length); // the data itself, positioned at x32 (won't be automatically found on load) Array.Copy(new byte[] { 0x32, 0, 0, 0x08 }, 0, buffer, 0x10, 4); // the pointer to the data. Pointer is aligned, but data is not. var model = new PokemonModel(buffer); var viewPort = new ViewPort("test.gba", model); // the act of searching should find the anchor var results = viewPort.Find("this is the song"); Assert.Single(results); Assert.Single(model.GetNextRun(0x32).PointerSources); }
public void UsingBackspaceMidStringMakesTheStringEndThere() { SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); for (int i = 0; i < 0x10; i++) { model[i] = 0x00; } // add an anchor with some data on the 2nd line viewPort.SelectionStart = new Point(0, 1); viewPort.Edit("^bob\"\" \"Hello World!\""); viewPort.SelectionStart = new Point(6, 1); viewPort.Edit(ConsoleKey.Backspace); Assert.Equal("\"Hello \"", PCSString.Convert(model, 0x10, PCSString.ReadString(model, 0x10, true))); }
public void UnnamedStringAnchorAutomaticallySearchesForPointersToAnchor() { var text = "This is the song that never ends."; var bytes = PCSString.Convert(text).ToArray(); SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); Array.Copy(bytes, 0, model.RawData, 0x32, bytes.Length); // the data itself, positioned at x32 (won't be automatically found on load) Array.Copy(new byte[] { 0x32, 0, 0, 0x08 }, 0, model.RawData, 0x10, 4); // the pointer to the data. Pointer is aligned, but data is not. // the act of dropping an anchor should search for pointers viewPort.SelectionStart = new Point(2, 3); viewPort.Edit("^\"\" "); Assert.Equal(0x32, model.GetNextRun(0x32).Start); Assert.Single(model.GetNextRun(0x32).PointerSources); }
public void CanNameExistingStringAnchor() { SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); var bytes = PCSString.Convert("Hello World!").ToArray(); model[0] = 0x08; model[1] = 0x00; model[2] = 0x00; model[3] = 0x08; Array.Copy(bytes, 0, model.RawData, 0x08, bytes.Length); viewPort.SelectionStart = new Point(0x08, 0); viewPort.Edit("^"); viewPort.Edit("bob\"\" "); Assert.Equal("bob", ((Pointer)viewPort[0, 0].Format).DestinationName); }
public void CanCutPasteArrayOverItself() { // arrange var delta = new ModelDelta(); var(model, viewPort) = (Model, ViewPort); var errors = new List <string>(); var elements = new[] { "123", "alice", "candy land", "hello world", "fortify" }; for (int i = 0; i < elements.Length; i++) { var content = PCSString.Convert(elements[i]); while (content.Count < 0x10) { content.Add(0x00); } Array.Copy(content.ToArray(), 0, model.RawData, 0x10 * i + 0x20, 0x10); } model.WritePointer(delta, 0x00, 0x20); model.ObserveRunWritten(delta, new PointerRun(0x00)); model.WritePointer(delta, 0x04, 0x90); model.ObserveRunWritten(delta, new PointerRun(0x04)); // the anchor at 0x90 should prevent a paste overwrite viewPort.SelectionStart = new Point(0, 2); viewPort.Edit("^testdata[name\"\"16]5 "); viewPort.Goto.Execute("000000"); viewPort.OnError += (sender, message) => errors.Add(message); // act -> cut var fileSystem = new StubFileSystem(); viewPort.SelectionStart = new Point(0, 2); viewPort.SelectionEnd = new Point(0xF, 6); // select all 5 elements viewPort.Copy.Execute(fileSystem); viewPort.Clear.Execute(); string text = fileSystem.CopyText; // act -> paste viewPort.SelectionStart = new Point(0, 2); viewPort.Edit(text); // assert: no errors Assert.Empty(errors); Assert.Equal("testdata", model.GetAnchorFromAddress(-1, 0x20)); }
public void FormatIsRemovedWhenEditingAnAnchor() { SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); var bytes = PCSString.Convert("Hello World!").ToArray(); model[0] = 0x08; model[1] = 0x00; model[2] = 0x00; model[3] = 0x08; Array.Copy(bytes, 0, model.RawData, 0x08, bytes.Length); viewPort.SelectionStart = new Point(0x08, 0); viewPort.Edit("^"); var underEdit = (UnderEdit)viewPort[8, 0].Format; Assert.Equal("^", underEdit.CurrentText); }
public void UsingTheAnchorEditorToSetStringFormatChangesVisibleData() { var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); var bytes = PCSString.Convert("Hello World!").ToArray(); Array.Copy(bytes, 0, buffer, 0x08, bytes.Length); var model = new PokemonModel(buffer); var viewPort = new ViewPort("test.txt", model) { Width = 0x10, Height = 0x10 }; viewPort.SelectionStart = new Point(0x08, 0); viewPort.Edit("^bob "); viewPort.AnchorText = "^bob\"\""; var anchor = (Anchor)viewPort[8, 0].Format; Assert.IsType <PCS>(anchor.OriginalFormat); }
public void UnnamedStringAnchorAutomaticallySearchesForPointersToAnchor() { var text = "This is the song that never ends."; var bytes = PCSString.Convert(text).ToArray(); var buffer = new byte[0x200]; Array.Copy(bytes, 0, buffer, 0x32, bytes.Length); // the data itself, positioned at x32 (won't be automatically found on load) Array.Copy(new byte[] { 0x32, 0, 0, 0x08 }, 0, buffer, 0x10, 4); // the pointer to the data. Pointer is aligned, but data is not. var model = new PokemonModel(buffer); var viewPort = new ViewPort("test.gba", model) { Width = 0x10, Height = 0x10 }; // the act of dropping an anchor should search for pointers viewPort.SelectionStart = new Point(2, 3); viewPort.Edit("^\"\" "); Assert.Equal(0x32, model.GetNextRun(0x32).Start); Assert.Single(model.GetNextRun(0x32).PointerSources); }
public void CopyAnUnnamedStringInsertsAName() { var text = "This is the song that never ends."; var bytes = PCSString.Convert(text).ToArray(); SetFullModel(0xFF); var(model, viewPort) = (Model, ViewPort); Array.Copy(bytes, 0, model.RawData, 0x30, bytes.Length); Array.Copy(new byte[] { 0x30, 0, 0, 0x08 }, 0, model.RawData, 0x10, 4); // the pointer to the data. Pointer is aligned, but data is not. var fileSystem = new StubFileSystem(); model.Load(model.RawData, null); ViewPort.Refresh(); viewPort.SelectionStart = new Point(0, 3); viewPort.ExpandSelection(0, 3); viewPort.Copy.Execute(fileSystem); Assert.Contains(".This", fileSystem.CopyText); Assert.Contains("\"\" \"This", fileSystem.CopyText); // format, then space, then start of text }
public void UsingBackspaceMidStringMakesTheStringEndThere() { var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); for (int i = 0; i < 0x10; i++) { buffer[i] = 0x00; } var model = new PokemonModel(buffer); var viewPort = new ViewPort("test.txt", model) { Width = 0x10, Height = 0x10 }; // add an anchor with some data on the 2nd line viewPort.SelectionStart = new Point(0, 1); viewPort.Edit("^bob\"\" \"Hello World!\""); viewPort.SelectionStart = new Point(6, 1); viewPort.Edit(ConsoleKey.Backspace); Assert.Equal("\"Hello \"", PCSString.Convert(model, 0x10, PCSString.ReadString(model, 0x10, true))); }
public void CanNameExistingStringAnchor() { var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); var bytes = PCSString.Convert("Hello World!").ToArray(); buffer[0] = 0x08; buffer[1] = 0x00; buffer[2] = 0x00; buffer[3] = 0x08; Array.Copy(bytes, 0, buffer, 0x08, bytes.Length); var model = new PokemonModel(buffer); var viewPort = new ViewPort("test.txt", model) { Width = 0x10, Height = 0x10 }; viewPort.SelectionStart = new Point(0x08, 0); viewPort.Edit("^"); viewPort.Edit("bob\"\" "); Assert.Equal("bob", ((Pointer)viewPort[0, 0].Format).DestinationName); }
public void CopyAnUnnamedStringInsertsAName() { var text = "This is the song that never ends."; var bytes = PCSString.Convert(text).ToArray(); var buffer = new byte[0x200]; Array.Copy(bytes, 0, buffer, 0x30, bytes.Length); Array.Copy(new byte[] { 0x30, 0, 0, 0x08 }, 0, buffer, 0x10, 4); // the pointer to the data. Pointer is aligned, but data is not. var model = new PokemonModel(buffer); var viewPort = new ViewPort("test.gba", model) { Width = 0x10, Height = 0x10 }; var fileSystem = new StubFileSystem(); viewPort.SelectionStart = new Point(0, 3); viewPort.ExpandSelection(0, 3); viewPort.Copy.Execute(fileSystem); Assert.Contains("^This", fileSystem.CopyText); Assert.Contains("\"\" \"This", fileSystem.CopyText); // format, then space, then start of text }