/// <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);
                    }
                }
            }
        }
Esempio n. 7
0
        /// <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();
        }