}// ----------------------------------------- // -- public override void start() { base.start(); p = (RestoreParams)jobData; INPUT = Path.Combine(p.tempDir, track.storedFileName); OUTPUT = Path.Combine(p.tempDir, track.getFilenameRaw()); track.workingFile = OUTPUT; // Point to the new file // - var fileExt = Path.GetExtension(track.storedFileName); requirePostSizeFix = AudioMaster.isLossyByExt(fileExt); // -- if (track.isData) { var ecm = new EcmTools(CDCRUSH.TOOLS_PATH); setupHandlers(ecm); ecm.unecm(INPUT); } else { // No need to convert back, end the task if (p.mode == 2) { // Point to correct file track.workingFile = INPUT; complete(); return; } var ffmp = new FFmpeg(CDCRUSH.FFMPEG_PATH); if (fileExt.ToLower() == ".tak") { var tak = new Tak(CDCRUSH.TOOLS_PATH); setupHandlers(tak); tak.decodeToStream(INPUT, (_out) => { ffmp.convertWavStreamToPCM(OUTPUT, (_in) => { _out.CopyTo(_in); _in.Close(); }); }); } else { setupHandlers(ffmp); ffmp.audioToPCM(INPUT, track.workingFile); } } log("Restoring track -" + track.storedFileName); }// -----------------------------------------
}// ----------------------------------------- // - public override void start() { RestoreParams p = jobData; LOG.line(); LOG.log("=== RESTORING A CD with the following parameters :"); LOG.log("- Input : {0}", p.inputFile); LOG.log("- Output Dir : {0}", p.outputDir); LOG.log("- Temp Dir : {0}", p.tempDir); LOG.log("- Force Single bin : {0}", p.flag_forceSingle); LOG.log("- Create subfolder : {0}", p.flag_folder); LOG.log("- Restore to encoded audio/.cue : {0}", p.flag_encCue); base.start(); }// -----------------------------------------
/// <summary> /// RESTORE An Archive file to target output folder /// </summary> /// <param name="_Input">Input file, Must be `.arc`</param> /// <param name="_Output">Output folder, If null, it will be same as input file folder</param> /// <param name="_Flag_Folder">Extract files to a subfolder of `_output`</param> /// <param name="_Mode">0: Normal, 1:Merge, 2:Restore To EncAudio</param> /// <param name="onComplete">(completeStatus)</param> /// <returns>Preliminary Success</returns> public static bool startJob_RestoreCD( string _Input, string _Output, bool _Flag_Folder, int _Mode, Action <bool> onComplete) { // NOTE : JOB checks for input file if (LOCKED) { ERROR = "Engine is working"; return(false); } if (!FFMPEG_OK) { ERROR = "FFmpeg is not set"; return(false); } LOCKED = true; var par = new RestoreParams { inputFile = _Input, // Checked in the JOB outputDir = _Output, // Checked in the JOB flag_folder = _Flag_Folder, mode = _Mode, expectedTracks = HACK_CD_TRACKS }; var j = new JobRestore(par); j.MAX_CONCURRENT = MAX_TASKS; j.onJobStatus = jobStatusHandler; // For status and progress updates j.onComplete = (s) => { LOCKED = false; ERROR = j.ERROR[1]; onComplete(s); }; j.start(); return(true); } // -----------------------------------------
}// ----------------------------------------- /// <summary> /// Called on FAIL / COMPLETE / PROGRAM EXIT /// Clean up temporary files /// </summary> protected override void kill() { base.kill(); if (CDCRUSH.FLAG_KEEP_TEMP) { return; } RestoreParams p = jobData; if (p.tempDir != p.outputDir) // NOTE: This is always a subdir of the master temp dir { try { Directory.Delete(p.tempDir, true); }catch (IOException) { // do nothing } } // -- }// -----------------------------------------
} // ----------------------------------------- /// <summary> /// RESTORE an arc file to target output folder /// </summary> /// <param name="_Input">Input file, Must be `.arc`</param> /// <param name="_Output">Output folder, If null, it will be same as input file folder</param> /// <param name="onComplete">(completeStatus)</param> /// <returns></returns> public static bool restoreARC(string _Input, string _Output, bool flag_folder, bool flag_forceSingle, Action <bool> onComplete) { // NOTE : JOB checks for input file if (LOCKED) { ERROR = "Engine is working"; return(false); } if (!FFMPEG_OK) { ERROR = "FFmpeg is not set"; return(false); } LOCKED = true; var par = new RestoreParams { inputFile = _Input, // Checked in the JOB outputDir = _Output, // Checked in the JOB flag_folder = flag_folder, flag_forceSingle = flag_forceSingle // SINGLE FILE }; var j = new JobRestore(par); j.MAX_CONCURRENT = MAX_TASKS; j.onComplete = (s) => { LOCKED = false; ERROR = j.ERROR[1]; onComplete(s); }; j.onJobStatus = jobStatusHandler; // For status and progress updates j.start(); return(true); } // -----------------------------------------
}// ----------------------------------------- // -- public override void start() { base.start(); p = (RestoreParams)jobData; log("Restoring track -" + track.storedFileName); // -- crushedTrackPath = Path.Combine(p.tempDir, track.storedFileName); // Set the final track pathname now, I need this for later. track.workingFile = Path.Combine(p.tempDir, track.getFilenameRaw()); // -- if (track.isData) { var ecm = new EcmTools(CDCRUSH.TOOLS_PATH); ecm.onComplete = (s) => { if (s) { deleteOldFile(); if (!checkTrackMD5()) { fail(msg: "MD5 checksum is wrong!"); return; } complete(); } else { fail(msg: ecm.ERROR); } }; ecm.unecm(crushedTrackPath); killExtra = () => ecm.kill(); } else { if (Path.GetExtension(track.storedFileName) == ".ogg") { isOgg = true; } else { isFlac = true; // must be flac } var ffmp = new FFmpeg(CDCRUSH.FFMPEG_PATH); ffmp.onComplete = (s) => { if (s) { deleteOldFile(); // Don't need it if (isOgg) { correctPCMSize(); } if (isFlac) { if (!checkTrackMD5()) { fail(msg: "MD5 checksum is wrong!"); return; } } complete(); } else { fail(msg: ffmp.ERROR); } }; ffmp.audioToPCM(crushedTrackPath); killExtra = () => ffmp.kill(); } }// -----------------------------------------
// -- public JobRestore(RestoreParams p) : base("Restore CD") { // Check for input files // -------------------- if (!CDCRUSH.check_file_(p.inputFile, CDCRUSH.CDCRUSH_EXTENSION)) { fail(msg: CDCRUSH.ERROR); return; } if (string.IsNullOrEmpty(p.outputDir)) { p.outputDir = Path.GetDirectoryName(p.inputFile); } // -- Output folder check if (p.flag_folder) { p.outputDir = CDCRUSH.checkCreateUniqueOutput(p.outputDir, Path.GetFileNameWithoutExtension(p.inputFile)); if (p.outputDir == null) { fail("Output Dir Error " + p.outputDir); return; } } else { if (!FileTools.createDirectory(p.outputDir)) { fail(msg: "Can't create Output Dir " + p.outputDir); return; } } // -- p.tempDir = Path.Combine(CDCRUSH.TEMP_FOLDER, Guid.NewGuid().ToString().Substring(0, 12)); if (!FileTools.createDirectory(p.tempDir)) { fail(msg: "Can't create TEMP dir"); return; } // Safeguard, even if the GUI doesn't allow it if (p.flag_encCue) { p.flag_forceSingle = false; } // IMPORTANT!! sharedData gets set by value, NOT A POINTER, do not make changes to p after this jobData = p; // -- hack_setExpectedProgTracks(p.expectedTracks + 3); // - Extract the Archive // ----------------------- add(new CTask((t) => { var arc = new FreeArc(CDCRUSH.TOOLS_PATH); t.handleCliReport(arc); arc.extractAll(p.inputFile, p.tempDir); arc.onProgress = (pr) => t.PROGRESS = pr; // In case the operation is aborted t.killExtra = () => { arc.kill(); }; }, "Extracting", "Extracting the archive to temp folder")); // - Read JSON data // - Restore tracks // - JOIN if it has to // ----------------------- add(new CTask((t) => { var cd = new CueReader(); jobData.cd = cd; // -- if (!cd.loadJson(Path.Combine(p.tempDir, CDCRUSH.CDCRUSH_SETTINGS))) { t.fail(msg: cd.ERROR); return; } LOG.log("== Detailed CD INFOS =="); LOG.log(cd.getDetailedInfo()); // - Push TASK RESTORE tasks right after this one foreach (CueTrack tr in cd.tracks) { addNextAsync(new TaskRestoreTrack(tr)); // Note: Task will take care of encoded cue case } //-- t.complete(); }, "-Preparing to Restore", "Reading stored CD info and preparing track restore tasks")); // - Join Tracks, but only when not creating .Cue/Enc Audio // ----------------------- if (!p.flag_encCue) { add(new CTask((t) => { CueReader cd = jobData.cd; // -- Join tracks if (p.flag_forceSingle || !cd.MULTIFILE) { // The task will read data from the shared job data var // Will join all tracks in place into track01.bin // Note: Sets track.workingFile to null to moved track addNext(new TaskJoinTrackFiles()); } //-- t.complete(); }, "-Preparing to Join")); } // - Prepare tracks `trackfile` which is the track written to the CUE // - Convert tracks // - Move files to final destination // - Create CUE files // - Delete Temp Files // ----------------------- add(new CTask((t) => { CueReader cd = jobData.cd; int progressStep = (int)Math.Round(100.0f / cd.tracks.Count); // -- foreach (var track in cd.tracks) { if (p.flag_encCue) { string ext = Path.GetExtension(track.workingFile); if (cd.tracks.Count == 1) { track.trackFile = $"{cd.CD_TITLE}{ext}"; } else { track.trackFile = $"{cd.CD_TITLE} (track {track.trackNo}){ext}"; } if (!cd.MULTIFILE) { track.setNewTimesReset(); // :: CONVERTS SINGLE TO MULTI } } else { if (p.flag_forceSingle && cd.MULTIFILE) // :: CONVERT MULTI TO SINGLE { track.setNewTimesBasedOnSector(); } if (cd.MULTIFILE && !p.flag_forceSingle) { track.trackFile = cd.CD_TITLE + " " + track.getFilenameRaw(); } if (!cd.MULTIFILE || p.flag_forceSingle) { if (track.trackNo == 1) { track.trackFile = cd.CD_TITLE + ".bin"; } else { track.trackFile = null; } } } // -- // Move ALL files to final output folder // NULL workingFile means that is has been deleted if (track.workingFile != null) { FileTools.tryMove(track.workingFile, Path.Combine(p.outputDir, track.trackFile)); t.PROGRESS += progressStep; } } // -- end track processing // -- // Create CUE File if (!cd.saveCUE(Path.Combine(p.outputDir, cd.CD_TITLE + ".cue"))) { t.fail(cd.ERROR); return; } t.complete(); }, "Moving, Finalizing", "Calculating track data and creating .CUE")); // - Complete - }// -----------------------------------------
}// ----------------------------------------- // -- public override void start() { base.start(); p = (RestoreParams)jobData; // -- crushedTrackPath = Path.Combine(p.tempDir, track.storedFileName); // Set the final track pathname now, I need this for later. track.workingFile = Path.Combine(p.tempDir, track.getFilenameRaw()); // -- if (track.isData) { var ecm = new EcmTools(CDCRUSH.TOOLS_PATH); ecm.onComplete = (s) => { ecm.onProgress = handleProgress; if (s) { deleteOldFile(); if (!checkTrackMD5()) { fail(msg: "MD5 checksum is wrong!"); return; } complete(); } else { fail(msg: ecm.ERROR); } }; ecm.unecm(crushedTrackPath); killExtra = () => ecm.kill(); } else { // No need to convert back if (p.flag_encCue) { // Fix current file track.workingFile = crushedTrackPath; complete(); return; } // -- isFlac = (Path.GetExtension(track.storedFileName) == ".flac"); var ffmp = new FFmpeg(CDCRUSH.FFMPEG_PATH); ffmp.onProgress = handleProgress; ffmp.onComplete = (s) => { if (s) { deleteOldFile(); // Don't need it if (!isFlac) { // OGG and MP3 don't restore to the exact byte length correctPCMSize(); } else { // FLAC restores to exact bytes if (!checkTrackMD5()) { fail(msg: "MD5 checksum is wrong!"); return; } } complete(); } else { fail(msg: ffmp.ERROR); } }; ffmp.audioToPCM(crushedTrackPath); killExtra = () => ffmp.kill(); } log("Restoring track -" + track.storedFileName); }// -----------------------------------------
// -- public JobRestore(RestoreParams p) : base("Restore CD") { // Check for input files // :: -------------------- if (!CDCRUSH.check_file_(p.inputFile, CDCRUSH.CDCRUSH_EXTENSION)) { fail(msg: CDCRUSH.ERROR); return; } if (string.IsNullOrEmpty(p.outputDir)) { p.outputDir = Path.GetDirectoryName(p.inputFile); } if (p.flag_folder) { try { p.outputDir = Path.Combine(p.outputDir, Path.GetFileNameWithoutExtension(p.inputFile)); } catch (ArgumentException) { fail("Output Dir Error " + p.outputDir); return; } } if (!FileTools.createDirectory(p.outputDir)) { fail(msg: "Can't create Output Dir " + p.outputDir); return; } p.tempDir = Path.Combine(CDCRUSH.TEMP_FOLDER, Guid.NewGuid().ToString().Substring(0, 12)); if (!FileTools.createDirectory(p.tempDir)) { fail(msg: "Can't create TEMP dir"); return; } // IMPORTANT!! sharedData gets set by value, NOT A POINTER, do not make changes to p after this jobData = p; // - Extract the Archive // ----------------------- add(new CTask((t) => { var arc = new FreeArc(CDCRUSH.TOOLS_PATH); t.handleCliReport(arc); arc.extractAll(p.inputFile, p.tempDir); // In case the operation is aborted t.killExtra = () => { arc.kill(); }; }, "Extracting", true)); // - Read JSON data // - Restore tracks // - JOIN if it has to // ----------------------- add(new CTask((t) => { var cd = new CueReader(); jobData.cd = cd; // This runs in sync: if (!cd.loadJson(Path.Combine(p.tempDir, CDCRUSH.CDCRUSH_SETTINGS))) { t.fail(msg: cd.ERROR); return; } //-- #if DEBUG cd.debugInfo(); #endif // - Push TASK RESTORE tasks right after this one foreach (CueTrack tr in cd.tracks) { addNextAsync(new TaskRestoreTrack(tr)); } //-- t.complete(); }, "Preparing to Restore")); // - Join Tracks // ----------------------- add(new CTask((t) => { CueReader cd = jobData.cd; // -- Join tracks if (p.flag_forceSingle || !cd.MULTIFILE) { // The task will read data from the shared job data var // Will join all tracks in place into track01.bin // Note: Sets track.workingFile to null to moved track addNext(new TaskJoinTrackFiles()); } //-- t.complete(); }, "Preparing to Join")); // - Prepare tracks `trackfile` which is the track written to the CUE // - Convert tracks // - Move files to final destination // - Create CUE files // - Delete Temp Files // ----------------------- add(new CTask((t) => { CueReader cd = jobData.cd; // -- foreach (var track in cd.tracks) { if (p.flag_forceSingle && cd.MULTIFILE) // :: CONVERT MULTI TO SINGLE { track.setNewTimesBasedOnSector(); } if (cd.MULTIFILE && !p.flag_forceSingle) { track.trackFile = cd.CD_TITLE + " " + track.getFilenameRaw(); } if (!cd.MULTIFILE || p.flag_forceSingle) { if (track.trackNo == 1) { track.trackFile = cd.CD_TITLE + ".bin"; } else { track.trackFile = null; } } // -- // Move ALL files to final output folder // NULL workingFile means that is has been deleted if (track.workingFile != null) { FileTools.tryMove(track.workingFile, Path.Combine(p.outputDir, track.trackFile)); } } // -- end track processing // -- // Create CUE File if (!cd.saveCUE(Path.Combine(p.outputDir, cd.CD_TITLE + ".cue"))) { t.fail(cd.ERROR); return; } t.complete(); }, "Moving, Finalizing")); // - Complete - }// -----------------------------------------
// -- public JobRestore(RestoreParams par) : base("Restore CD") { p = par; // -- hack_setExpectedProgTracks(p.expectedTracks + 3); // - Extract the Archive // ----------------------- add(new CTask((t) => { var arc = ArchiveMaster.getArchiver(p.inputFile); t.handleProcessStatus(arc); arc.extract(p.inputFile, p.tempDir); arc.onProgress = (pr) => t.PROGRESS = pr; // In case the operation is aborted t.killExtra = () => { arc.kill(); }; }, "Extracting", "Extracting the archive to temp folder")); // - Read JSON data // - Restore tracks // - JOIN if it has to // ----------------------- add(new CTask((t) => { p.cd = new cd.CDInfos(); try{ p.cd.jsonLoad(Path.Combine(p.tempDir, CDCRUSH.CDCRUSH_SETTINGS)); }catch (haxe.lang.HaxeException e) { t.fail(msg: e.Message); return; } jobData = p; LOG.log("== Detailed CD INFOS =="); LOG.log(p.cd.getDetailedInfo()); for (int i = 0; i < p.cd.tracks.length; i++) { // Push TASK RESTORE tasks right after this one // Note: Task will take care of encoded cue case addNextAsync(new TaskRestoreTrack(p.cd.tracks[i] as cd.CDTrack)); } //-- t.complete(); }, "-Preparing to Restore", "Reading stored CD info and preparing track restore tasks")); // - Join all Tracks together // ------------------------- if (p.mode < 2) { add(new CTask((t) => { // -- Join tracks if (p.mode == 1 || !p.cd.MULTIFILE) { // The task will read data from the shared job data var // Will join all tracks in place into track01.bin // Note: Sets track.workingFile to null to moved track addNext(new TaskJoinTrackFiles()); } //-- t.complete(); }, "-Preparing to Join")); } // - Prepare tracks `trackfile` which is the track written to the CUE // - Convert tracks // - Move files to final destination // - Create CUE files // - Delete Temp Files // ----------------------- add(new CTask((t) => { cd.CDInfos CD = p.cd; int progressStep = (int)Math.Round(100.0f / CD.tracks.length); // -- for (int i = 0; i < CD.tracks.length; i++) { cd.CDTrack track = CD.tracks[i] as cd.CDTrack; string ext = Path.GetExtension(track.workingFile); if (p.mode == 2) // Restore to encoded audio { if (CD.tracks.length == 1) { track.trackFile = $"{CD.CD_TITLE}{ext}"; } else { track.trackFile = $"{CD.CD_TITLE} (track {track.trackNo}){ext}"; } if (!CD.MULTIFILE) { track.rewriteIndexes_forMultiFile(); // :: CONVERTS SINGLE TO MULTI } } else { if (p.mode == 1 && CD.MULTIFILE) // :: CONVERT MULTI TO SINGLE { track.rewriteIndexes_forSingleFile(); } if (CD.MULTIFILE && p.mode != 1) { track.trackFile = $"{CD.CD_TITLE} (track {track.trackNo}){ext}"; } if (!CD.MULTIFILE || p.mode == 1) { if (track.trackNo == 1) { track.trackFile = CD.CD_TITLE + ".bin"; } else { track.trackFile = null; } } } // -- // Move ALL files to final output folder // NULL workingFile means that is has been deleted if (track.workingFile != null) { FileTools.tryMove(track.workingFile, Path.Combine(p.outputDir, track.trackFile)); t.PROGRESS += progressStep; } } // -- end track processing // -- // Create CUE File try{ CD.cueSave( Path.Combine(p.outputDir, CD.CD_TITLE + ".cue"), new haxe.root.Array <object>(new [] { "CDCRUSH (dotNet) version : " + CDCRUSH.PROGRAM_VERSION, CDCRUSH.LINK_SOURCE })); }catch (haxe.lang.HaxeException e) { t.fail(msg: e.Message); return; } t.complete(); }, "Moving, Finalizing", "Calculating track data and creating .CUE")); // - Complete - }// -----------------------------------------