private async void OnSelectFileTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) { var item = (sender as FrameworkElement).DataContext as FileOpenFailureItem; var f = await PickMediaFileAsync(); if (f != null) { FutureAccessListHelper.Instance.AddTempItem(new IStorageItem[] { f }); var cueFile = _failedItems[item.FullPath]; Added.AddRange( await FileOpen.HandleFileWithCue(f, await CueFile.CreateFromFileAsync(cueFile, false))); _failedItems.Remove(item.FullPath); _items.Remove((from i in _items where string.Compare(i.FullPath, item.FullPath, true) == 0 select i).First()); if (_failedItems.Count == 0) { Hide(); } } }
private bool LoadTimelineDiff(CueFile cueFile, bool save = true) { if (save) { timeline.Export(); } timeline.DeleteAllTargets(); timeline.RemoveAllRepeaters(); foreach (Cue cue in cueFile.cues) { timeline.AddTargetFromAction(timeline.GetTargetDataForCue(cue)); } if (cueFile.NRCueData != null) { if (cueFile.NRCueData.pathBuilderNoteData.Count == cueFile.NRCueData.pathBuilderNoteCues.Count) { for (int i = 0; i < cueFile.NRCueData.pathBuilderNoteCues.Count; ++i) { var data = timeline.GetTargetDataForCue(cueFile.NRCueData.pathBuilderNoteCues[i]); data.pathBuilderData = cueFile.NRCueData.pathBuilderNoteData[i]; data.pathBuilderData.parentNotes.Add(data); //Recalculate the notes, and remove any identical enties that would have been loaded through the cues ChainBuilder.CalculateChainNotes(data); foreach (TargetData genData in data.pathBuilderData.generatedNotes) { var foundData = timeline.FindTargetData(genData.time, genData.behavior, genData.handType); if (foundData != null) { timeline.DeleteTargetFromAction(foundData); } } timeline.AddTargetFromAction(data); //Generate the notes, so the song is complete ChainBuilder.GenerateChainNotes(data); } } if (Timeline.audioLoaded) { foreach (var section in cueFile.NRCueData.repeaterSections) { timeline.AddRepeaterSectionFromAction(section); } } else { timeline.loadRepeaterSectionAfterAudio = cueFile.NRCueData.repeaterSections; } } return(true); }
public static MergedBin MergeBins(string file, IEnumerable <CueFile> cueFiles, string tempPath) { var mergedBin = new MergedBin(); mergedBin.CueFiles = new List <CueFile>(); Console.WriteLine($"Merging .bins..."); long currentFrame = 0; var mergedFilename = Path.GetFileNameWithoutExtension(file) + " - MERGED.bin"; mergedBin.Path = Path.Combine(tempPath, mergedFilename); var mcueFile = new CueFile() { FileName = mergedFilename, FileType = "BINARY", Tracks = new List <CueTrack>() }; mergedBin.CueFiles.Add(mcueFile); using (var joinedFile = new FileStream(mergedBin.Path, FileMode.Create)) { foreach (var cueFile in cueFiles) { using (var srcStream = new FileStream(Path.Combine(tempPath, cueFile.FileName), FileMode.Open)) { srcStream.CopyTo(joinedFile); foreach (var item in cueFile.Tracks) { var indexes = new List <CueIndex>(); foreach (var idx in item.Indexes) { var newIndex = new CueIndex(); newIndex.Number = idx.Number; newIndex.Position = idx.Position + Helper.PositionFromFrames(currentFrame); indexes.Add(newIndex); } var newTrack = new CueTrack() { DataType = item.DataType, Number = item.Number, Indexes = indexes }; mcueFile.Tracks.Add(newTrack); } var frames = srcStream.Length / 2352; currentFrame += frames; } } } return(mergedBin); }
public static async Task <string> HandleCueFileOpen(StorageFile cueFile, List <StorageFile> files, List <MusicPlaybackItem> add) { try { var parent = await cueFile.GetParentAsync(); var cue = await CueFile.CreateFromFileAsync(cueFile, false); // Check user-opened files that are in the same directory var audioTrack = (from f in files where string.Compare(cue.FileName, f.Name, true) == 0 select f).FirstOrDefault(); StorageFile file = audioTrack; // Cannot find suitable audio track in user opened files. if (file == null) { // Check parent directory if (parent != null && !string.IsNullOrWhiteSpace(cue.FileName)) { file = await parent.TryGetItemAsync(cue.FileName) as StorageFile; } // Try opening the file else if (!string.IsNullOrWhiteSpace(cue.FileName)) { var parentPath = cueFile.Path.Substring(0, cueFile.Path.Length - Path.GetFileName(cueFile.Path).Length); var audioTrackPath = Path.Combine(parentPath, cue.FileName); file = await NativeMethods.GetStorageFileFromPathAsync(audioTrackPath) as StorageFile; } else { return(null); } } // Otherwise, remove that. else { files.Remove(audioTrack); } if (file != null) { add.AddRange(await HandleFileWithCue(file, cue)); } else { return(Path.Combine( Directory.GetParent(cueFile.Path).FullName, cue.FileName)); } } catch { } return(null); }
public void Export() { string dirpath = Application.persistentDataPath; CueFile export = new CueFile(); export.cues = new List <Cue>(); foreach (Target target in orderedNotes) { if (target.data.beatLength == 0) { target.data.beatLength = 120; } if (target.data.behavior == TargetBehavior.Metronome) { continue; } export.cues.Add(NotePosCalc.ToCue(target, offset, false)); } switch (difficultyManager.loadedIndex) { case 0: audicaFile.diffs.expert = export; break; case 1: audicaFile.diffs.advanced = export; break; case 2: audicaFile.diffs.moderate = export; break; case 3: audicaFile.diffs.beginner = export; break; } audicaFile.desc = desc; AudicaExporter.ExportToAudicaFile(audicaFile); NotificationShower.AddNotifToQueue(new NRNotification("Map saved successfully!")); }
static MergedBin MergeBins(string file, CueFile cueFilex, string tempPath) { var mergedBin = new MergedBin(); mergedBin.CueFile = new CueFile(); var cueFilePath = Path.GetDirectoryName(file); long currentFrame = 0; var mergedFilename = Path.GetFileNameWithoutExtension(file) + " - MERGED.bin"; mergedBin.Path = Path.Combine(tempPath, mergedFilename); var mcueFile = new CueFileEntry() { FileName = mergedFilename, FileType = "BINARY", Tracks = new List <CueTrack>() }; mergedBin.CueFile.FileEntries.Add(mcueFile); using (var joinedFile = new FileStream(mergedBin.Path, FileMode.Create)) { foreach (var cueFileEntry in cueFilex.FileEntries) { var binPath = cueFileEntry.FileName; if (Path.GetDirectoryName(binPath) == "" || Path.GetDirectoryName(binPath).StartsWith("..") || Path.GetDirectoryName(binPath).StartsWith(".")) { binPath = Path.Combine(cueFilePath, cueFileEntry.FileName); } using (var srcStream = new FileStream(binPath, FileMode.Open)) { srcStream.CopyTo(joinedFile); foreach (var item in cueFileEntry.Tracks) { var indexes = new List <CueIndex>(); foreach (var idx in item.Indexes) { var newIndex = new CueIndex { Number = idx.Number, Position = idx.Position + TOCHelper.PositionFromFrames(currentFrame) }; indexes.Add(newIndex); } var newTrack = new CueTrack() { DataType = item.DataType, Number = item.Number, Indexes = indexes }; mcueFile.Tracks.Add(newTrack); } var frames = srcStream.Length / 2352; currentFrame += frames; } } } return(mergedBin); }
public static async Task <List <MusicPlaybackItem> > GetPlaybackItemsFromFilesAsync(IReadOnlyList <IStorageItem> items) { var files = new List <StorageFile>(); var added = new List <MusicPlaybackItem>(); // [| (cue file, full file path of missing file) |] var failedCue = new List <Tuple <StorageFile, string> >(); foreach (var file in await GetAllFiles(items)) { files.Add(file); } var listFiles = PickAndRemoveCueM3uWplFiles(files); foreach (var cueFile in listFiles.Item1) { try { var failedFileName = await HandleCueFileOpen(cueFile, files, added); if (failedFileName != null) { failedCue.Add(new Tuple <StorageFile, string>(cueFile, failedFileName)); } } catch { } } foreach (var m3uFile in listFiles.Item2) { await HandleM3uAsync(m3uFile, added); } foreach (var wplFile in listFiles.Item3) { await HandleWplAsync(wplFile, added); } foreach (var file in files) { try { IMediaInfo info = null; using (var stream = await file.OpenAsync(FileAccessMode.Read)) { NativeMethods.GetMediaInfoFromStream(stream, out info); } if (info == null) { continue; } var cue = info.AllProperties["cuesheet"]; if (!string.IsNullOrWhiteSpace(cue)) { var cueItems = await HandleFileWithCue(file, CueFile.CreateFromString(cue)); added.AddRange(cueItems); continue; } var prop = await file.GetBasicPropertiesAsync(); var internalEntity = DbMediaFile.FromMediaInfo(info, prop.DateModified); internalEntity.IsExternal = true; internalEntity.Path = file.Path; internalEntity.Id = -65535; if (string.IsNullOrWhiteSpace(internalEntity.Title) && !string.IsNullOrWhiteSpace(internalEntity.Path)) { internalEntity.Title = Path.GetFileNameWithoutExtension(internalEntity.Path); } added.Add(MusicPlaybackItem.CreateFromMediaFile(internalEntity)); } catch { } } if (failedCue.Count > 0) { added.AddRange( await FileOpenFailure.AddFailedFilePath( failedCue)); } return(added); }
public static async Task <IEnumerable <MusicPlaybackItem> > HandleFileWithCue(StorageFile file, CueFile cue) { //Cue files will only have track number var items = cue.Indices .OrderBy(c => ParseWithDefaultFallback( c.TrackInfo.TrackNumber)); List <MusicPlaybackItem> files = new List <MusicPlaybackItem>(); IMediaInfo info = null; using (var stream = await file.OpenAsync(FileAccessMode.Read)) { NativeMethods.GetMediaInfoFromStream( stream, out info); } if (info == null) { return(files); } var prop = await file.GetBasicPropertiesAsync(); foreach (ManagedAudioIndexCue item in items) { if (item.TrackInfo.Duration == TimeSpan.Zero) { (item.TrackInfo as CueMediaInfo).Duration = item.Duration = info.Duration - item.StartTime; } var internalEntity = DbMediaFile.FromMediaInfo(item.TrackInfo, prop.DateModified); internalEntity.IsExternal = true; internalEntity.StartTime = (int)item.StartTime.TotalMilliseconds; internalEntity.Path = file.Path; internalEntity.Id = -65535; files.Add(MusicPlaybackItem.CreateFromMediaFile(internalEntity)); } return(files); }
public void LoadFromString(string cueString) { TextReader tr = new StringReader(cueString); bool track_has_pregap = false; bool track_has_postgap = false; int last_index_num = -1; CueFile currFile = null; CueTrack currTrack = null; for (; ; ) { string line = tr.ReadLine(); if (line == null) break; line = line.Trim(); if (line == "") continue; var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpper(); switch (key) { case "REM": break; case "FILE": { currTrack = null; currFile = new CueFile(); Files.Add(currFile); currFile.Path = clp.ReadPath().Trim('"'); if (!clp.EOF) { string temp = clp.ReadToken().ToUpper(); switch (temp) { case "BINARY": currFile.FileType = CueFileType.Binary; break; case "WAVE": case "MP3": currFile.FileType = CueFileType.Wave; break; } currFile.StrFileType = temp; } break; } case "TRACK": { if (currFile == null) throw new CueBrokenException("invalid cue structure"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); string strtracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(strtracknum, out tracknum)) throw new CueBrokenException("malformed track number"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); if (tracknum < 0 || tracknum > 99) throw new CueBrokenException("`All track numbers must be between 1 and 99 inclusive.`"); string strtracktype = clp.ReadToken().ToUpper(); currTrack = new CueTrack(); switch (strtracktype) { case "MODE1/2352": currTrack.TrackType = ETrackType.Mode1_2352; break; case "MODE1/2048": currTrack.TrackType = ETrackType.Mode1_2048; break; case "MODE2/2352": currTrack.TrackType = ETrackType.Mode2_2352; break; case "AUDIO": currTrack.TrackType = ETrackType.Audio; break; default: throw new CueBrokenException("unhandled track type"); } currTrack.TrackNum = tracknum; currFile.Tracks.Add(currTrack); track_has_pregap = false; track_has_postgap = false; last_index_num = -1; break; } case "INDEX": { if (currTrack == null) throw new CueBrokenException("invalid cue structure"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); if (track_has_postgap) throw new CueBrokenException("`The POSTGAP command must appear after all INDEX commands for the current track.`"); string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum)) throw new CueBrokenException("malformed index number"); if (clp.EOF) throw new CueBrokenException("invalid cue structure (missing index timestamp)"); string str_timestamp = clp.ReadToken(); if (indexnum < 0 || indexnum > 99) throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`"); if (indexnum != 1 && indexnum != last_index_num + 1) throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`"); last_index_num = indexnum; CueTrackIndex cti = new CueTrackIndex(indexnum) { Timestamp = new Timestamp(str_timestamp), IndexNum = indexnum }; currTrack.Indexes[indexnum] = cti; break; } case "PREGAP": if (track_has_pregap) throw new CueBrokenException("`Only one PREGAP command is allowed per track.`"); if (currTrack.Indexes.Count > 0) throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`"); currTrack.PreGap = new Timestamp(clp.ReadToken()); track_has_pregap = true; break; case "POSTGAP": if (track_has_postgap) throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`"); track_has_postgap = true; currTrack.PostGap = new Timestamp(clp.ReadToken()); break; case "CATALOG": case "PERFORMER": case "SONGWRITER": case "TITLE": case "ISRC": //TODO - keep these for later? break; default: throw new CueBrokenException("unsupported cue command: " + key); } } //end cue parsing loop }
public void LoadFromString(string cueString) { TextReader tr = new StringReader(cueString); bool track_has_pregap = false; bool track_has_postgap = false; int last_index_num = -1; CueFile currFile = null; CueTrack currTrack = null; for (; ;) { string line = tr.ReadLine(); if (line == null) { break; } line = line.Trim(); if (line == "") { continue; } var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpper(); switch (key) { case "REM": break; case "FILE": { currTrack = null; currFile = new CueFile(); Files.Add(currFile); currFile.Path = clp.ReadPath().Trim('"'); if (!clp.EOF) { string temp = clp.ReadToken().ToUpper(); switch (temp) { case "BINARY": currFile.FileType = CueFileType.Binary; break; case "WAVE": case "MP3": currFile.FileType = CueFileType.Wave; break; } currFile.StrFileType = temp; } break; } case "TRACK": { if (currFile == null) { throw new CueBrokenException("invalid cue structure"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } string strtracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(strtracknum, out tracknum)) { throw new CueBrokenException("malformed track number"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } if (tracknum < 0 || tracknum > 99) { throw new CueBrokenException("`All track numbers must be between 1 and 99 inclusive.`"); } string strtracktype = clp.ReadToken().ToUpper(); currTrack = new CueTrack(); switch (strtracktype) { case "MODE1/2352": currTrack.TrackType = ETrackType.Mode1_2352; break; case "MODE1/2048": currTrack.TrackType = ETrackType.Mode1_2048; break; case "MODE2/2352": currTrack.TrackType = ETrackType.Mode2_2352; break; case "AUDIO": currTrack.TrackType = ETrackType.Audio; break; default: throw new CueBrokenException("unhandled track type"); } currTrack.TrackNum = tracknum; currFile.Tracks.Add(currTrack); track_has_pregap = false; track_has_postgap = false; last_index_num = -1; break; } case "INDEX": { if (currTrack == null) { throw new CueBrokenException("invalid cue structure"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } if (track_has_postgap) { throw new CueBrokenException("`The POSTGAP command must appear after all INDEX commands for the current track.`"); } string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum)) { throw new CueBrokenException("malformed index number"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure (missing index timestamp)"); } string str_timestamp = clp.ReadToken(); if (indexnum < 0 || indexnum > 99) { throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`"); } if (indexnum != 1 && indexnum != last_index_num + 1) { throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`"); } last_index_num = indexnum; CueTrackIndex cti = new CueTrackIndex(indexnum) { Timestamp = new Timestamp(str_timestamp), IndexNum = indexnum }; currTrack.Indexes[indexnum] = cti; break; } case "PREGAP": if (track_has_pregap) { throw new CueBrokenException("`Only one PREGAP command is allowed per track.`"); } if (currTrack.Indexes.Count > 0) { throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`"); } currTrack.PreGap = new Timestamp(clp.ReadToken()); track_has_pregap = true; break; case "POSTGAP": if (track_has_postgap) { throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`"); } track_has_postgap = true; currTrack.PostGap = new Timestamp(clp.ReadToken()); break; case "CATALOG": case "PERFORMER": case "SONGWRITER": case "TITLE": case "ISRC": //TODO - keep these for later? break; default: throw new CueBrokenException("unsupported cue command: " + key); } } //end cue parsing loop }
public void ExportAsCues() { //Pick folder //string prevDir = PlayerPrefs.GetString("recentDirCues", ""); string diff; switch (DifficultyManager.I.loadedIndex) { case 0: diff = "Expert"; break; case 1: diff = "Advanced"; break; case 2: diff = "Standard"; break; default: diff = "Easy"; break; } string fileName = Path.GetFileName(Timeline.audicaFile.filepath)?.Replace(".audica", ""); fileName = fileName + "_NRExport-" + diff + ".cues"; string path; if (!String.IsNullOrEmpty(NRSettings.config.cuesSavePath)) { path = Path.Combine(NRSettings.config.cuesSavePath, fileName); } else { path = StandaloneFileBrowser.SaveFilePanel("Find community_maps/maps folder in Audica folder", Path.Combine(Application.dataPath, @"../"), fileName, "cues"); if (String.IsNullOrEmpty(path)) { return; } NRSettings.config.cuesSavePath = Path.GetDirectoryName(path); NRSettings.SaveSettingsJson(); } //Ensure all chains are generated List <TargetData> nonGeneratedNotes = new List <TargetData>(); foreach (Target note in Timeline.instance.notes) { if (note.data.behavior == TargetBehavior.NR_Pathbuilder && note.data.pathBuilderData.createdNotes == false) { nonGeneratedNotes.Add(note.data); } } foreach (var data in nonGeneratedNotes) { ChainBuilder.GenerateChainNotes(data); } CueFile export = new CueFile(); export.cues = new List <Cue>(); export.NRCueData = new NRCueData(); foreach (Target target in Timeline.orderedNotes) { if (target.data.beatLength == 0) { target.data.beatLength = Constants.SixteenthNoteDuration; } if (target.data.behavior == TargetBehavior.Metronome) { continue; } var cue = NotePosCalc.ToCue(target, Timeline.offset); if (target.data.behavior == TargetBehavior.NR_Pathbuilder) { export.NRCueData.pathBuilderNoteCues.Add(cue); export.NRCueData.pathBuilderNoteData.Add(target.data.pathBuilderData); continue; } export.cues.Add(cue); } File.WriteAllText(path, JsonUtility.ToJson(export)); NotificationShower.Queue(new NRNotification("Saved cues!")); }
public static string CuesToJson(CueFile cueFile) { return(JsonUtility.ToJson(cueFile, true)); }
private void ConvertMultiToSingleBin_OnClick(object sender, RoutedEventArgs e) { var openFileDialog = new Ookii.Dialogs.Wpf.VistaOpenFileDialog(); openFileDialog.Filter = "Supported files|*.bin;*.cue|All files|*.*"; openFileDialog.Multiselect = true; var openResult = openFileDialog.ShowDialog(); if (!openResult.GetValueOrDefault(false)) { return; } var saveFileDialog = new Ookii.Dialogs.Wpf.VistaSaveFileDialog(); saveFileDialog.Filter = "Supported files|*.bin;"; saveFileDialog.DefaultExt = ".bin"; saveFileDialog.AddExtension = true; var saveResult = saveFileDialog.ShowDialog(); if (!saveResult.GetValueOrDefault(false)) { return; } bool generatedCue = false; string tempFile = ""; var trackRegex = new Regex("Track (\\d+)"); if (openFileDialog.FileNames.Length > 1) { if (!openFileDialog.FileNames.All(f => { var match = trackRegex.Match(f); return(Path.GetExtension(f).ToLower() == ".bin" && match.Success && int.TryParse(match.Groups[1].Value, out var dummy)); })) { MessageBox.Show(Window, "Please multi-select only .bins ending in (Track #)", "PSXPackager", MessageBoxButton.OK, MessageBoxImage.Information); return; } var cueFile = new CueFile(); var index = 1; foreach (var fileName in openFileDialog.FileNames.OrderBy(f => int.Parse(trackRegex.Match(f).Groups[1].Value))) { cueFile.FileEntries.Add(new CueFileEntry() { FileName = fileName, FileType = "BINARY", Tracks = index == 1 ? new List <CueTrack>() { new CueTrack() { DataType = CueTrackType.Data, Number = index, Indexes = new List <CueIndex>() { new CueIndex() { Number = 1, Position = new IndexPosition(0, 0, 0) } } } } : new List <CueTrack>() { new CueTrack() { DataType = CueTrackType.Audio, Number = index, Indexes = new List <CueIndex>() { new CueIndex() { Number = 0, Position = new IndexPosition(0, 0, 0) }, new CueIndex() { Number = 1, Position = new IndexPosition(0, 2, 0) } } } } }); index++; } tempFile = Path.GetTempFileName() + ".cue"; CueFileWriter.Write(cueFile, tempFile); generatedCue = true; } else if (Path.GetExtension(openFileDialog.FileName).ToLower() == ".cue") { tempFile = openFileDialog.FileName; } else { MessageBox.Show(Window, "Please select the CUE file, or if you do not have a CUE file, multi-select all the .bins ending in (Track #)", "PSXPackager", MessageBoxButton.OK, MessageBoxImage.Information); } var folder = Path.GetDirectoryName(Path.GetFullPath(saveFileDialog.FileName)); var filename = Path.GetFileName(saveFileDialog.FileName); var processing = new Popstation.Processing(null, null, null); var(binfile, cuefile) = processing.ProcessCue(tempFile, Path.GetTempPath()); var cueFileName = Path.GetFileNameWithoutExtension(filename) + ".cue"; var outputPath = Path.Combine(folder, saveFileDialog.FileName); if (File.Exists(outputPath)) { File.Delete(outputPath); } File.Move(binfile, outputPath); if (generatedCue) { var updatedCueFile = CueFileReader.Read(cuefile); var fileEntry = updatedCueFile.FileEntries.First(); fileEntry.FileName = filename; CueFileWriter.Write(updatedCueFile, Path.Combine(folder, cueFileName)); } MessageBox.Show(Window, $"Merged .bins to {outputPath}", "PSXPackager", MessageBoxButton.OK, MessageBoxImage.Information); }