Exemple #1
0
 public async Task ParseDebugDataAsync(AsmProject project)
 {
     var debugDataTask = project.ParseDebugDataAsync();
     await debugDataTask.ContinueWith(t => { if (OnDebugDataUpdated != null)
                                             {
                                                 OnDebugDataUpdated();
                                             }
                                      });
 }
Exemple #2
0
        private List <BuildError> BuildSync(AsmProject project, TaskCompletionSource <bool> buildProcessSource)
        {
            var errors        = new List <BuildError>();
            var asmFailed     = false;
            var cartridge     = project.CurrentConfiguration;
            var ideFolder     = Program.WorkingDirectory;
            var projectFolder = project.Directory.FullName;

            var linkerConfig = project.Files.FirstOrDefault(f => f.GetRelativePath() == cartridge.LinkerConfigFile);

            if (linkerConfig == null)
            {
                var configFiles = project.Files.Where(l => l.Mode == CompileMode.LinkerConfig);
                if (!configFiles.Any())
                {
                    buildProcessSource.SetResult(false);
                    return(errors);
                }
                linkerConfig = configFiles.First();
            }

            Status("Building NES cartridge...");

            Log(new LogData("Building NES cartridge...", LogType.Headline));
            Log(new LogData("Processing content pipeline"));

            var skippedCount = 0;

            foreach (var file in project.Files.Where(f => f.Mode == CompileMode.ContentPipeline && f.Pipeline != null))
            {
                // Get a fresh File Info from the file system in case something was changed since we loaded the project
                var fileInfo = new FileInfo(file.File.FullName);                 // TODO: Refresh ProjectFile.File using file system watch to get LastWriteTime
                // ?
                if (!fileInfo.Exists)
                {
                    var error = new BuildError(
                        string.Format(@"File '{0}' is missing. Skipping data processing...", file.GetRelativePath()),
                        BuildError.BuildErrorType.Warning);
                    errors.Add(error);
                    ErrorReceived(error);
                    continue;
                }

                if (file.Pipeline.LastProcessed != null && file.Pipeline.LastProcessed > fileInfo.LastWriteTime)
                {
                    //Log(new LogData(string.Format("Skipping '{0}'", file.GetRelativePath())));
                    skippedCount++;
                    continue;
                }

                Log(new LogData(string.Format("Building {0}", string.Join(",", file.Pipeline.OutputFiles.Where(f => f != null).Select(project.GetRelativePath)))));
                file.Pipeline.Process();
            }
            if (skippedCount > 0)
            {
                Log(new LogData(string.Format("Skipped {0} unchanged files", skippedCount)));
            }

            if (projectFolder == null)
            {
                throw new Exception("Project directory not found");
            }

            var targetFile  = Path.Combine(projectFolder, cartridge.Filename);
            var buildFolder = Path.Combine(projectFolder, cartridge.BuildPath);
            var prgFolder   = cartridge.PrgBuildPath != null?Path.Combine(projectFolder, cartridge.PrgBuildPath) : null;

            var chrFolder = cartridge.ChrBuildPath != null?Path.Combine(projectFolder, cartridge.ChrBuildPath) : null;

            var debugFile = Path.Combine(projectFolder, cartridge.DebugFile);

            if (!Directory.Exists(Path.GetDirectoryName(targetFile)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
            }
            if (!Directory.Exists(buildFolder))
            {
                Directory.CreateDirectory(buildFolder);
            }
            if (prgFolder != null && !Directory.Exists(prgFolder))
            {
                Directory.CreateDirectory(prgFolder);
            }
            if (chrFolder != null && !Directory.Exists(chrFolder))
            {
                Directory.CreateDirectory(chrFolder);
            }
            if (!Directory.Exists(Path.GetDirectoryName(debugFile)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(debugFile));
            }

            if (cartridge.ChrBanks.Any() && chrFolder != null)
            {
                Log(new LogData("Building CHR from graphic banks", LogType.Headline));
                BuildChr(projectFolder, project, cartridge);
            }

            Log(new LogData("Building PRG from source files", LogType.Headline));

            var asmParams = GetAsmParams(projectFolder);

            var sourceFiles = project.Files.Where(f => f.Mode == CompileMode.IncludeInAssembly);
            var objectFiles = new List <string>();

            foreach (var projectFile in sourceFiles)
            {
                using (var asmProcess = new Process())
                {
                    asmProcess.StartInfo           = asmParams;
                    asmProcess.EnableRaisingEvents = true;
                    asmProcess.OutputDataReceived += OutputReceived;
                    string multilineError = null;
                    asmProcess.ErrorDataReceived += (s, e) => { ProcessErrorData(e.Data, errors, ref multilineError); };

                    var directory = Path.Combine(projectFolder, cartridge.BuildPath, projectFile.GetRelativeDirectory());
                    if (!Directory.Exists(directory))
                    {
                        Directory.CreateDirectory(directory);
                    }
                    var fileBase       = projectFile.GetRelativeDirectory() + @"/" + Path.GetFileNameWithoutExtension(projectFile.File.Name);
                    var sourceFile     = projectFile.GetRelativePath();
                    var objectFile     = cartridge.BuildPath + @"/" + fileBase + ".o";
                    var dependencyFile = cartridge.BuildPath + @"/" + fileBase + ".d";
                    objectFiles.Add(objectFile);

                    asmProcess.StartInfo.Arguments = string.Format("\"{0}\" -o \"{1}\" --create-dep \"{2}\" -t nes --cpu {3}{4} -g -I .",
                                                                   sourceFile,
                                                                   objectFile,
                                                                   dependencyFile,
                                                                   project.GetTargetCpu(),
                                                                   string.Join("", cartridge.Symbols.Select(s => " -D " + s)));

                    Log(new LogData("ca65 " + asmProcess.StartInfo.Arguments));
                    asmProcess.Start();
                    asmProcess.BeginOutputReadLine();
                    asmProcess.BeginErrorReadLine();
                    asmProcess.WaitForExit();
                    if (asmProcess.ExitCode != 0)
                    {
                        asmFailed = true;
                    }
                }
            }
            if (asmFailed)
            {
                buildProcessSource.SetResult(false);
                return(errors);
            }

            if (File.Exists(targetFile))
            {
                File.Delete(targetFile);
            }
            var outputFile = targetFile;

            if (cartridge.PrgBuildPath != null && cartridge.PrgFile != null)
            {
                outputFile = Path.Combine(projectFolder, cartridge.PrgBuildPath, cartridge.PrgFile);
            }

            using (var linkerProcess = new Process())
            {
                linkerProcess.StartInfo           = asmParams;
                linkerProcess.EnableRaisingEvents = true;
                linkerProcess.OutputDataReceived += OutputReceived;
                string multilineError = null;
                linkerProcess.ErrorDataReceived += (s, e) => { ProcessErrorData(e.Data, errors, ref multilineError); };

                //linkerProcess.StartInfo.FileName = string.Format(@"{0}\cc65\bin\cl65.exe", ideFolder);
                //linkerProcess.StartInfo.Arguments = string.Format("-t nes -C {0} --mapfile {1} -Wl --dbgfile,{2} -o {3} {4}", cartridge.LinkerConfigFile, cartridge.MapFile, cartridge.DebugFile, prgFile, string.Join(" ", objectFiles));

                linkerProcess.StartInfo.FileName  = string.Format(@"{0}\cc65\bin\ld65.exe", ideFolder);
                linkerProcess.StartInfo.Arguments = string.Format("-o \"{3}\" -C \"{0}\" -m \"{1}\" --dbgfile \"{2}\" {4}",
                                                                  linkerConfig.GetRelativePath(),
                                                                  cartridge.MapFile,
                                                                  cartridge.DebugFile,
                                                                  outputFile,
                                                                  string.Join(" ", objectFiles.Select(f => string.Format("\"{0}\"", f)))
                                                                  );

                Log(new LogData("ld65 " + linkerProcess.StartInfo.Arguments));
                linkerProcess.Start();
                linkerProcess.BeginOutputReadLine();
                linkerProcess.BeginErrorReadLine();
                linkerProcess.WaitForExit();
                if (linkerProcess.ExitCode != 0)
                {
                    buildProcessSource.SetResult(false);
                    return(errors);
                }
            }

            if (project.Type == ProjectType.Snes && cartridge.CalculateChecksum)
            {
                using (var stream = File.Open(outputFile, FileMode.Open, FileAccess.ReadWrite))
                {
                    Log(new LogData("Calculating SNES checksum", LogType.Headline));

                    var prgSize      = (Int32)stream.Length;        // We don't expect the PRG size to ever be longer than a 32 bit int
                    var prgData      = new byte[prgSize];
                    var checksumData = new byte[4];

                    stream.Read(prgData, 0, prgSize);

                    ushort checksum = 0;
                    unchecked             // Allow 16bit integer to overflow
                    {
                        for (var i = 0; i < prgSize; i++)
                        {
                            if (i >= 0x7FDC && i <= 0x7FDF)
                            {
                                continue;
                            }
                            checksum += prgData[i];
                        }
                        checksum += 0xff;
                        checksum += 0xff;
                    }
                    checksumData[2] = (byte)(checksum & 0xff);
                    checksumData[3] = (byte)((checksum >> 8) & 0xff);
                    checksumData[0] = (byte)(~checksum & 0xff);
                    checksumData[1] = (byte)((~checksum >> 8) & 0xff);

                    stream.Position = 0x7FDC;
                    stream.Write(checksumData, 0, 4);
                }
            }

            if (prgFolder != null)
            {
                if (project.Type == ProjectType.Nes)
                {
                    Log(new LogData("Merging into iNES file", LogType.Headline));
                }
                using (var write = File.OpenWrite(targetFile))
                {
                    using (var read = File.OpenRead(string.Format(@"{0}\{1}", prgFolder, cartridge.PrgFile)))
                    {
                        var prgSize = (Int32)read.Length;                         // We don't expect the PRG size to ever be longer than a 32 bit int
                        var prgData = new byte[prgSize];

                        read.Read(prgData, 0, prgSize);
                        write.Write(prgData, 0, prgSize);
                    }
                    if (chrFolder != null)
                    {
                        using (var read = File.OpenRead(string.Format(@"{0}\{1}", chrFolder, cartridge.ChrFile)))
                        {
                            var chrSize = (Int32)read.Length;                     // We don't expect the CHR size to ever be longer than a 32 bit int
                            var chrData = new byte[chrSize];
                            read.Read(chrData, 0, chrSize);
                            write.Write(chrData, 0, chrSize);
                        }
                    }
                    write.Close();
                    Log(new LogData(targetFile.Replace('/', '\\')));
                }
            }
            Log(new LogData("---"));
            Log(new LogData("Build complete\r\n"));
            Status("Build complete");

            Log(new LogData("Parsing debug info"));
            var debugDataTask = project.ParseDebugDataAsync(debugFile);

            debugDataTask.ContinueWith((t) =>
            {
                if (t.Status != TaskStatus.RanToCompletion)
                {
                    Log(new LogData("Error parsing: " + t.Exception.InnerExceptions[0].Message, LogType.Error));
                    buildProcessSource.SetResult(false);
                    return;
                }
                if (OnDebugDataUpdated != null)
                {
                    OnDebugDataUpdated();
                }
                Log(new LogData("Done parsing"));

                project.UpdatedSinceLastBuild = false;
                buildProcessSource.SetResult(true);
            });

            return(errors);
        }