}// ----------------------------------------- /* * Reads a CUE file, line by line, used in load(); * CUE SHEET INFO : http://wiki.hydrogenaud.io/index.php?title=Cue_sheet * This is not a complete Cue Parser, just a basic one * that can read game disks. A track of data and tracks of CDDA audio. */ int cue_parser(string line) { // Get FILE image name if (line.ToUpper().StartsWith("FILE")) { openTrack = null; // Close any open Track // Try to get the filename within the quotes after FILE var ff = Regex.Split(line, "[\"\']"); if (ff.Length < 3) { // Because Array should be like [] {"FILE","GAME NAME.BIN", ... ,"BINARY"} ERROR = "Could not get filename"; return(0); } // [SAFEGUARD] if (!new string[] { "BINARY", "WAVE", "MP3" }.Contains(ff.Last().ToUpper().Trim())) { ERROR = "Unsupported File Type, or identifier missing. Supported = {BINARY,WAVE,MP3}"; return(0); } // First and last elements are not needed openFile = string.Join("'", ff, 1, ff.Length - 2); return(1); // No reason to check for other cases } // -- FILE // Get Track NO and track TYPE if (line.ToUpper().StartsWith("TRACK")) { openTrack = null; // Close any open Track Match m = Regex.Match(line, @"^\s*TRACK\s+(\d+)\s+(\S+)", RegexOptions.IgnoreCase); if (m.Success) { int trackNo = int.Parse(m.Groups[1].Value); string trackType = m.Groups[2].Value.ToUpper(); // [SAFEGUARD] Check to see if Track Type is valid if (getSectorsByDataType(trackType) == 0) { ERROR = "Unsupported TRACK type:" + trackType; return(0); } // [SAFEGUARD] Check to see if the trackNO is already defined in the tracks foreach (var t in tracks) { if (t.trackNo == trackNo) { ERROR = "TRACK Number already defined"; return(0); } } CueTrack tr = new CueTrack(trackNo, trackType); openTrack = tr; tr.trackFile = openFile; openFile = null; tracks.Add(tr); } else { ERROR = "Cannot parse TRACK line"; return(0); } return(1); } // -- TRACK // -- if (line.ToUpper().StartsWith("INDEX")) { if (openTrack == null) { ERROR = "INDEX not inside a TRACK"; return(0); } Match m = Regex.Match(line, @"^\s*INDEX\s+(\d+)\s+(\d{1,2}):(\d{1,2}):(\d{1,2})", RegexOptions.IgnoreCase); if (m.Success) { var indexNo = int.Parse(m.Groups[1].Value); if (openTrack.indexExists(indexNo)) { ERROR = String.Format("Duplicate Index on track {0}", openTrack.trackNo); return(0); } openTrack.addIndex(indexNo, int.Parse(m.Groups[2].Value), int.Parse(m.Groups[3].Value), int.Parse(m.Groups[4].Value)); } else { ERROR = "Cannot parse INDEX line"; return(0); } return(1); } // -- INDEX // -- if (line.ToUpper().StartsWith("PREGAP")) { if (openTrack == null) { ERROR = "INDEX not inside a TRACK"; return(0); } Match m = Regex.Match(line, @"^\s*PREGAP\s+(\d{1,2}):(\d{1,2}):(\d{1,2})", RegexOptions.IgnoreCase); if (m.Success) { openTrack.setGap(int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value), int.Parse(m.Groups[3].Value)); } else { ERROR = "Cannot parse PREGAP line"; return(0); } return(1); } // - PREGAP // -- // Title can be either at ROOT is used as the CD title, or inside a Track if (line.ToUpper().StartsWith("TITLE")) { // Currently only used as CD title, and only works with titles inside "" if (openTrack == null) { Match m = Regex.Match(line, ".+\"(.+)\"$"); if (m.Success) { CD_TITLE = m.Value; } else { ERROR = "Can't parse TITLE"; return(0); } } } // -- TITLE // Skip Comments // if (line.ToUpper().StartsWith("REM")) return 1; -- Everything else will return 1 return(1); }// -----------------------------------------
}// ----------------------------------------- /// <summary> /// Load a previously saved JSON settings file /// </summary> /// <param name="file">The JSON file to load</param> /// <returns>Success 1 OK,0 ERROR</returns> public bool loadJson(string file) { if (!File.Exists(file)) { ERROR = "File:" + file + "does not exist"; return(false); } string loadedJSONText; try{ loadedJSONText = File.ReadAllText(file); } catch (IOException e) { ERROR = e.ToString(); return(false); } catch (UnauthorizedAccessException) { ERROR = "Unauthorized Access"; return(false); } // -- JSON Get var js = new JavaScriptSerializer(); // Note: Should never throw error because it was created internally Dictionary <string, object> data = js.Deserialize <Dictionary <string, object> >(loadedJSONText); // -- Version check, defaults to 1 if field missing int versionLoaded = 1; if (data.ContainsKey("version")) { versionLoaded = int.Parse(data["version"].ToString()); } // -- Backwards Compatibility :: switch (versionLoaded) { case 1: // Convert V1 to V2 int ssize = (int)data["sectorSize"]; foreach (Dictionary <string, object> t in (ArrayList)data["tracks"]) { t["diskFileSize"] = (int)t["sectorSize"] * ssize; t["diskFile"] = null; t["isData"] = (bool)!((t["type"] as string) == "AUDIO"); } goto case 2; case 2: // Convert V2 to V3 string CDT = null; // If any is data, then set to the data type. int diskFiles = 0; // Count tracks with diskfiles set bool anyAudio = false; // Is there any audio track? foreach (Dictionary <string, object> t in (ArrayList)data["tracks"]) { t["trackType"] = t["type"]; t["indexes"] = t["indexAr"]; // Array is the same t["storedFileName"] = t["filename"]; t["byteSize"] = t["diskFileSize"]; t["md5"] = ""; // New field if (!anyAudio && t["trackType"].ToString() == "AUDIO") { anyAudio = true; } // Capture the first data format of any track: if ((bool)t["isData"] && CDT == null) { CDT = t["trackType"].ToString(); } if (t["diskFile"] != null) { diskFiles++; } } // New/Renamed properties : data["audio"] = anyAudio ? "??? kbps" : ""; data["cdType"] = CDT ?? "AUDIO"; data["totalSize"] = data["imageSize"]; data["multiFile"] = (diskFiles > 1) && (diskFiles == (data["tracks"] as ArrayList).Count); break; default: break; } // -- At this point the data is properly in v3 format. // -- Read tracks :: tracks = new List <CueTrack>(); foreach (Dictionary <string, object> t in (ArrayList)data["tracks"]) { var tr = new CueTrack((int)t["trackNo"], t["trackType"].ToString()); tr.sectorSize = (int)t["sectorSize"]; tr.sectorStart = (int)t["sectorStart"]; tr.byteSize = (int)t["byteSize"]; tr.pregapMinutes = (int)t["pregapMinutes"]; tr.pregapSeconds = (int)t["pregapSeconds"]; tr.pregapMillisecs = (int)t["pregapMillisecs"]; tr.storedFileName = (string)t["storedFileName"]; tr.md5 = (string)t["md5"]; foreach (Dictionary <string, object> ind in (ArrayList)t["indexes"]) { tr.addIndex((int)ind["no"], (int)ind["minutes"], (int)ind["seconds"], (int)ind["millisecs"]); } tracks.Add(tr); } // -- cd info data :: CD_TITLE = data["cdTitle"].ToString(); CD_TYPE = data["cdType"].ToString(); CD_TOTAL_SIZE = (int)data["totalSize"]; CD_AUDIO_QUALITY = data["audio"].ToString(); MULTIFILE = (bool)data["multiFile"]; SECTOR_SIZE = (int)data["sectorSize"]; // Some Checks :: if (tracks[0].byteSize == 0) { calculateTracksByteSize(); // Single File Multi Tracks } if (tracks.Count > 1 && tracks[1].sectorStart == 0) { calculateTracksSectorStart(); // Multi File Multi Track } return(true); }// -----------------------------------------
}// ----------------------------------------- /// <summary> /// Load a descriptor file /// The CD title is read from the .CUE filename /// Also checks if files declared inside the .CUE exist or not /// </summary> /// <param name="file">Filename to load [.cue]</param> /// <returns>Success ( read the ERROR Property )</returns> public bool load(string file) { if (!File.Exists(file)) { ERROR = "File:" + file + "does not exist"; return(false); } // Vars init loadedFile_path = file; loadedFile_dir = Path.GetDirectoryName(loadedFile_path); loadedFile_ext = Path.GetExtension(loadedFile_path).ToLower().Trim('.'); // This is redudant.. if (!SUPPORTED_FORMATS.Contains(loadedFile_ext)) { ERROR = "File Type is not supported.- Supported formats : " + SUPPORTED_FORMATS.ToString(); return(false); } try { loadedFile = File.ReadAllLines(file); } catch (IOException) { ERROR = "There was an I/O problem loading the file"; return(false); } // Init vars CD_TOTAL_SIZE = 0; CD_TITLE = "untitled CD"; SECTOR_SIZE = 0; CD_TYPE = null; tracks = new List <CueTrack>(); openTrack = null; openFile = null; // Try to capture the CD TITLE from the CUE filename Match m1 = Regex.Match(loadedFile_path, @"([^\/\\]*)\.(?:" + SUPPORTED_FORMATS + @")$", RegexOptions.IgnoreCase); if (m1.Success) { CD_TITLE = m1.Groups[1].Value; } // Start Parsing the file based on it's type Func <string, int> parser; switch (loadedFile_ext) { case "cue": parser = cue_parser; break; case "ccd": parser = ccd_parser; break; default: parser = cue_parser; break; } // Parser :: for (int c = 0; c < loadedFile.Length; c++) { loadedFile[c] = loadedFile[c].Trim(); // Trim whitespaces if (loadedFile[c].Length == 0) { continue; // Skip blank lines } if (loadedFile[c] == "\n") { continue; } if (parser(loadedFile[c]) == 0) { ERROR = String.Format("Parse Error at line[{0}] : {1}", c + 1, ERROR); return(false); } } // -- POST PARSE CHECK :: if (tracks.Count == 0) { ERROR = "No Tracks in the CUE file"; return(false); } getCDTypeFromTracks(); // :: Some Debug Info LOG.log("[CUEREADER] : Loading : `{0}`", loadedFile_path); LOG.log("[CUEREADER] : Title : `{0}`, Type: `{1}`, NumberOfTracks:{2}", CD_TITLE, CD_TYPE, tracks.Count); LOG.indent(1); // -- if (loadedFile_ext == "ccd") { // TODO: CCD } // :: Go through each and every track, regardless if multitrack or not, // Check for every single one of the files if exist or not // Also count the number of file images to figure out `multifilecd` int cc = 0; // Number of tracks with DiskFiles found foreach (var tr in tracks) { if (tr.indexes.Count == 0) { ERROR = "Track [" + tr.trackNo + "] has no indexes defined"; return(false); } if (tr.trackFile == null) { continue; } cc++; tr.workingFile = Path.Combine(loadedFile_dir, tr.trackFile); // Check the diskfiles if (!File.Exists(tr.workingFile)) { ERROR = "Image \"" + tr.trackFile + "\" does not exist"; return(false); } // Get Sizes var finfo = new FileInfo(Path.Combine(loadedFile_dir, tr.trackFile)); tr.byteSize = (int)finfo.Length; // it can't be more than 800MB, so it's safe tr.sectorSize = (int)Math.Ceiling((double)(tr.byteSize / SECTOR_SIZE)); // -- if (tr.sectorSize <= 0) { // Rare but worth checking ERROR = "DiskFile " + tr.trackFile + " is currupt"; return(false); } CD_TOTAL_SIZE += tr.byteSize; } // -- end each track // :: POST PARSE CALCULATIONS AND CHECKS :: // - Is it MultiTrack with MultiFiles? if (cc == tracks.Count && cc > 1) { LOG.log("+ MULTI-FILE Image CD"); MULTIFILE = true; // Need to set sectorStart at each tracks; calculateTracksSectorStart(); } else if (cc == 1) { LOG.log("+ SINGLE-FILE Image CD"); MULTIFILE = false; // In case an single was found but not at the first track: if (tracks[0].trackFile == null) { ERROR = "First track must declare an image file"; return(false); } var imageSectorSize = tracks[0].sectorSize; //Calculate tracks, starting from the end, backwards to 0 var c = tracks.Count - 1; //Calculate last track manually, out of the loop tracks[c].calculateStart(); tracks[c].sectorSize = imageSectorSize - tracks[c].sectorStart; while (--c >= 0) { tracks[c].calculateStart(); tracks[c].sectorSize = tracks[c + 1].sectorStart - tracks[c].sectorStart; } calculateTracksByteSize(); } else if (cc == 0) { ERROR = "There are no image files declared in the sheet."; return(false); } else { // Rare, and I don't know if anything does this ERROR = "Multiple Image sheets are restricted to one Track per Image only"; return(false); } LOG.log("+ Total CD SIZE : {0}", CD_TOTAL_SIZE); foreach (var tt in tracks) { LOG.log(tt); } LOG.line(); LOG.indent(0); return(true); }// -----------------------------------------