/// <summary> /// Displays the Export Z80 Code dialog to collect parameter data /// </summary> /// <param name="vm">View model with collected data</param> /// <returns> /// True, if the user stars export; false, if the export is cancelled /// </returns> private bool DisplayExportParameterDialog(out ExportZ80ProgramViewModel vm) { var exportDialog = new ExportZ80ProgramDialog { HasMaximizeButton = false, HasMinimizeButton = false }; var programName = Path.GetFileNameWithoutExtension(CompiledItemPath) ?? "MyCode"; var filename = Path.Combine(Package.Options.CodeExportPath, $"{programName}.tzx"); vm = new ExportZ80ProgramViewModel { Format = ExportFormat.Tzx, Name = programName, Filename = filename, SingleBlock = true, AddToProject = false, AutoStart = true, ApplyClear = true, StartAddress = ExportStartAddress.ToString() }; exportDialog.SetVm(vm); var accepted = exportDialog.ShowModal(); if (!accepted.HasValue || !accepted.Value) { IsCancelled = true; return(true); } return(false); }
public void SettingNameAndFileNameMakesVmValid() { // --- Arrange var vm = new ExportZ80ProgramViewModel(); // --- Act vm.Name = "MyCode"; vm.Filename = "MyCode.tzx"; // --- Assert vm.IsValid.ShouldBeTrue(); }
/// <summary> /// Save data blocks /// </summary> /// <param name="vm">Export parameters</param> /// <param name="blocksToSave">Collection of data blocks to save</param> private static void SaveDataBlocks(ExportZ80ProgramViewModel vm, IEnumerable <byte[]> blocksToSave) { try { // --- Create directory var dirName = Path.GetDirectoryName(vm.Filename); if (dirName != null && !Directory.Exists(dirName)) { Directory.CreateDirectory(dirName); } // --- Save data blocks if (vm.Format == ExportFormat.Tzx) { using (var writer = new BinaryWriter(File.Create(vm.Filename))) { var header = new TzxHeader(); header.WriteTo(writer); foreach (var block in blocksToSave) { var tzxBlock = new TzxStandardSpeedDataBlock { Data = block, DataLength = (ushort)block.Length }; tzxBlock.WriteTo(writer); } } } else { using (var writer = new BinaryWriter(File.Create(vm.Filename))) { foreach (var block in blocksToSave) { writer.Write((ushort)block.Length); writer.Write(block); } } } } catch (Exception ex) { VsxDialogs.Show($"An error has been raised while exporting a program: {ex.Message}", "Errorr when exporting file"); } }
public void EmptyFilenameMakesVmInvalid() { // --- Arrange var vm = new ExportZ80ProgramViewModel(); vm.Name = "MyCode"; vm.Filename = "MyCode.tzx"; var before = vm.IsValid; // --- Act vm.Filename = " \t \n "; // --- Assert before.ShouldBeTrue(); vm.IsValid.ShouldBeFalse(); }
public void ConstructionWorks() { // --- Act var vm = new ExportZ80ProgramViewModel(); // --- Assert vm.ShouldNotBeNull(); vm.AddToProject.ShouldBeFalse(); vm.ApplyClear.ShouldBeFalse(); vm.AutoStart.ShouldBeFalse(); vm.AssemblerOutput.ShouldBeNull(); vm.AutoStart.ShouldBeFalse(); vm.IsValid.ShouldBeFalse(); vm.Name.ShouldBeNull(); vm.Filename.ShouldBeNull(); vm.Format.ShouldBe((ExportFormat)0); vm.SingleBlock.ShouldBeFalse(); }
/// <summary> /// Save data blocks /// </summary> /// <param name="vm">Export parameters</param> /// <param name="blocksToSave">Collection of data blocks to save</param> private static void SaveDataBlocks(ExportZ80ProgramViewModel vm, IEnumerable <byte[]> blocksToSave) { // --- Create directory var dirName = Path.GetDirectoryName(vm.Filename); if (dirName != null && !Directory.Exists(dirName)) { Directory.CreateDirectory(dirName); } // --- Save data blocks if (vm.Format == ExportFormat.Tzx) { using (var writer = new BinaryWriter(File.Create(vm.Filename))) { var header = new TzxHeader(); header.WriteTo(writer); foreach (var block in blocksToSave) { var tzxBlock = new TzxStandardSpeedDataBlock { Data = block, DataLength = (ushort)block.Length }; tzxBlock.WriteTo(writer); } } } else { using (var writer = new BinaryWriter(File.Create(vm.Filename))) { foreach (var block in blocksToSave) { writer.Write((ushort)block.Length); writer.Write(block); } } } }
/// <summary> /// Compiles the program code /// </summary> /// <returns>True, if compilation successful; otherwise, false</returns> public async Task <bool> CompileCode() { // --- Execute pre-build event var codeManager = SpectNetPackage.Default.CodeManager; PrebuildError = null; PostbuildError = null; CleanupError = null; PreexportError = null; PostexportError = null; var eventOutput = await codeManager.RunPreBuildEvent(ItemFullPath); if (eventOutput != null) { PrebuildError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); return(false); } var start = DateTime.Now; var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); await pane.WriteLineAsync(_compiler.ServiceName); var output = await _compiler.CompileDocument(ItemFullPath, PrepareAssemblerOptions()); var duration = (DateTime.Now - start).TotalMilliseconds; await pane.WriteLineAsync($"Compile time: {duration}ms"); var compiled = output != null; if (compiled) { // --- Sign that compilation was successful HostPackage.DebugInfoProvider.CompiledOutput = Output = output; CreateCompilationListFile(_hierarchy, _itemId); } HostPackage.CodeManager.RaiseCompilationCompleted(output); // --- Execute post-build event if (compiled && output.Errors.Count == 0) { // --- Export if required if (!string.IsNullOrEmpty(SpectNetPackage.Default.Options.ExportOnCompile)) { var parts = SpectNetPackage.Default.Options.ExportOnCompile.Split(';'); var name = Path.GetFileNameWithoutExtension(ItemPath) ?? "MyCode"; var vm = new ExportZ80ProgramViewModel { Name = name, Filename = Path.Combine(Path.GetDirectoryName(SpectNetPackage.Default.ActiveProject.Root.FullName), EXPORT_PATH, name) }; if (parts.Length > 0) { // --- Validate format var format = parts[0].Trim().ToLower(); if (format == "tap") { vm.Format = ExportFormat.Tap; } else if (format == "tzx") { vm.Format = ExportFormat.Tzx; } else if (format == "hex") { vm.Format = ExportFormat.IntelHex; } else { PostbuildError = $"Invalid export format: {parts[0]}"; } } if (PostbuildError == null && parts.Length > 1) { var flag = parts[1].Trim(); if (flag == "" || flag == "1") { vm.AutoStart = true; } else if (flag == "0") { vm.AutoStart = false; } else { PostbuildError = $"Invalid export Auto-Start flag (use 0 or 1): {parts[1]}"; } } if (PostbuildError == null && parts.Length > 2) { var flag = parts[2].Trim(); if (flag == "" || flag == "1") { vm.ApplyClear = true; } else if (flag == "0") { vm.ApplyClear = false; } else { PostbuildError = $"Invalid export Apply CLEAR flag (use 0 or 1): {parts[2]}"; } } vm.SingleBlock = true; vm.AddToProject = false; if (PostbuildError == null && parts.Length > 3) { var flag = parts[3].Trim(); if (flag == "" || flag == "0") { vm.AddPause0 = false; } else if (flag == "1") { vm.AddPause0 = true; } else { PostbuildError = $"Invalid export Add PAUSE flag (use 0 or 1): {parts[3]}"; } } if (PostbuildError == null && parts.Length > 4 && parts[4].Trim() != "") { if (ushort.TryParse(parts[4], out var startAddr)) { vm.StartAddress = startAddr.ToString(); } else { PostbuildError = $"Invalid Start Address (use 0-65535): {parts[4]}"; } } if (PostbuildError == null && parts.Length > 5 && parts[5].Trim() != "") { if (ushort.TryParse(parts[5], out var border)) { if (border >= 0 && border <= 7) { vm.Border = border.ToString(); } else { PostbuildError = $"Invalid Border value (use 0-7): {parts[5]}"; } } else { PostbuildError = $"Invalid Border value (use 0-7): {parts[5]}"; } } if (PostbuildError == null && parts.Length > 6) { vm.ScreenFile = parts[6]; } if (PostbuildError == null) { ExportProgramCommand.ExportCompiledCode(Output, vm); } } // --- Run post-build tasks eventOutput = await codeManager.RunPostBuildEvent(ItemFullPath); if (eventOutput != null) { PostbuildError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); return(false); } } else { CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); } return(compiled); }
/// <summary> /// Exports the specified compiler output /// </summary> /// <param name="output">Compiler output</param> /// <param name="programName">Name of the program</param> /// <param name="vm">Export options</param> /// <returns></returns> public static int ExportCompiledCode(AssemblerOutput output, ExportZ80ProgramViewModel vm) { var oldExt = vm.Format; vm.Format = ExportFormat.Unknown; vm.Format = oldExt; if (vm.Format == ExportFormat.IntelHex) { SaveIntelHexFile(vm.Filename, output); return(0); } // --- Check for screen file error var useScreenFile = !string.IsNullOrEmpty(vm.ScreenFile) && vm.ScreenFile.Trim().Length > 0; if (useScreenFile && !CommonTapeFilePlayer.CheckScreenFile(vm.ScreenFile)) { return(1); } // --- Step #6: Create code segments var codeBlocks = CreateTapeBlocks(output, vm.Name, vm.SingleBlock); List <byte[]> screenBlocks = null; if (useScreenFile) { screenBlocks = CreateScreenBlocks(vm.ScreenFile); } // --- Step #7: Create Auto Start header block, if required var blocksToSave = new List <byte[]>(); if (!ushort.TryParse(vm.StartAddress, out var startAddress)) { startAddress = (ushort)(output == null ? -1 : output.ExportEntryAddress ?? output.EntryAddress ?? output.Segments[0].StartAddress); } if (vm.AutoStartEnabled) { var autoStartBlocks = CreateAutoStartBlock( output, vm.Name, useScreenFile, vm.AddPause0, vm.Border, startAddress, vm.ApplyClear ? output.Segments.Min(s => s.StartAddress) : (ushort?)null); blocksToSave.AddRange(autoStartBlocks); } // --- Step #8: Save all the blocks if (screenBlocks != null) { blocksToSave.AddRange(screenBlocks); } blocksToSave.AddRange(codeBlocks); SaveDataBlocks(vm, blocksToSave); return(0); }
/// <summary> /// Adds the exported file to the project structure /// </summary> /// <param name="vm">Export parameters</param> private void AddExportedFileToProject(ExportZ80ProgramViewModel vm) { var folderSegments = Package.Options.TapeFolder.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); foreach (var segment in folderSegments) { bool valid; try { valid = !Path.IsPathRooted(segment); } catch { valid = false; } if (!valid) { VsxDialogs.Show("The tape folder specified in the Options dialog " + "contains invalid characters or an absolute path. Go to the Options dialog and " + "fix the issue so that you can add the tape file to the project.", "Invalid characters in path"); return; } } // --- Obtain the project and its items var project = Package.CodeDiscoverySolution.CurrentProject.Root; var projectItems = project.ProjectItems; var currentIndex = 0; var find = true; while (currentIndex < folderSegments.Length) { // --- Find or create folder segments var segment = folderSegments[currentIndex]; if (find) { // --- We are in "find" mode var found = false; // --- Search for the folder segment foreach (ProjectItem projItem in projectItems) { var folder = projItem.Properties.Item("FolderName").Value?.ToString(); if (string.Compare(folder, segment, StringComparison.InvariantCultureIgnoreCase) == 0) { // --- We found the folder, we'll go no with search within this segment projectItems = projItem.ProjectItems; found = true; break; } } if (!found) { // --- Move to "create" mode find = false; } } if (!find) { // --- We're in create mode, add and locate the new folder segment var found = false; projectItems.AddFolder(segment); var parent = projectItems.Parent; if (parent is Project projectType) { projectItems = projectType.ProjectItems; } else if (parent is ProjectItem itemType) { projectItems = itemType.ProjectItems; } foreach (ProjectItem projItem in projectItems) { var folder = projItem.Properties.Item("FolderName").Value?.ToString(); if (string.Compare(folder, segment, StringComparison.InvariantCultureIgnoreCase) == 0) { // --- We found the folder, we'll go no with search within this segment projectItems = projItem.ProjectItems; found = true; break; } } if (!found) { // --- This should not happen... VsxDialogs.Show($"The folder segment {segment} could not be created.", "Adding project item failed"); return; } } // --- Move to the next segment currentIndex++; } // --- Check if that filename exists within the project folder var tempFile = Path.GetFileName(vm.Filename); ProjectItem toDelete = null; foreach (ProjectItem projItem in projectItems) { var file = Path.GetFileName(projItem.FileNames[0]); if (string.Compare(file, tempFile, StringComparison.InvariantCultureIgnoreCase) == 0) { var answer = VsxDialogs.Show("The exported tape file exists in the project. " + "Would you like to override it?", "File already exists", MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1); if (answer == VsxDialogResult.No) { return; } toDelete = projItem; break; } } // --- Remove existing file toDelete?.Delete(); // --- Add the item to the appropriate item projectItems.AddFromFileCopy(vm.Filename); // --- Refresh the solution's content Package.CodeDiscoverySolution.CurrentProject.CollectItems(); }