public async Task ParseDebugDataAsync(AsmProject project) { var debugDataTask = project.ParseDebugDataAsync(); await debugDataTask.ContinueWith(t => { if (OnDebugDataUpdated != null) { OnDebugDataUpdated(); } }); }
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); }