/// <summary> /// Serializes the project and writes it to the specified file. /// </summary> /// <param name="proj">Project to serialize.</param> /// <param name="pathName">Output path name.</param> /// <param name="errorMessage">Human-readable error string, or an empty string if all /// went well.</param> /// <returns>True on success.</returns> public static bool SerializeToFile(DisasmProject proj, string pathName, out string errorMessage) { try { string serializedData = SerializableProjectFile1.SerializeProject(proj); if (ADD_CRLF) { // Add some line breaks. This looks awful, but it makes text diffs // much more useful. serializedData = TextUtil.NonQuoteReplace(serializedData, "{", "{\r\n"); serializedData = TextUtil.NonQuoteReplace(serializedData, "},", "},\r\n"); } // Check to see if the project file is read-only. We want to fail early // so we don't leave our .TMP file sitting around -- the File.Delete() call // will fail if the destination is read-only. if (File.Exists(pathName) && (File.GetAttributes(pathName) & FileAttributes.ReadOnly) != 0) { throw new IOException(string.Format(Res.Strings.ERR_FILE_READ_ONLY_FMT, pathName)); } // The BOM is not required or recommended for UTF-8 files, but anecdotal // evidence suggests that it's sometimes useful. Shouldn't cause any harm // to have it in the project file. The explicit Encoding.UTF8 argument // causes it to appear -- WriteAllText normally doesn't. // // Write to a temp file, then rename over original after write has succeeded. string tmpPath = pathName + ".TMP"; File.WriteAllText(tmpPath, serializedData, Encoding.UTF8); if (File.Exists(pathName)) { File.Delete(pathName); } File.Move(tmpPath, pathName); errorMessage = string.Empty; return(true); } catch (Exception ex) { errorMessage = ex.Message; return(false); } }
/// <summary> /// Reads the specified file and deserializes it into the project. /// </summary> /// <param name="pathName">Input path name.</param> /// <param name="proj">Project to deserialize into.</param> /// <param name="report">File load report, which may contain errors or warnings.</param> /// <returns>True on success.</returns> public static bool DeserializeFromFile(string pathName, DisasmProject proj, out FileLoadReport report) { Debug.WriteLine("Deserializing '" + pathName + "'"); report = new FileLoadReport(pathName); string serializedData; try { serializedData = File.ReadAllText(pathName); } catch (Exception ex) { report.Add(FileLoadItem.Type.Error, Res.Strings.ERR_PROJECT_LOAD_FAIL + ": " + ex.Message); return(false); } if (serializedData.StartsWith(SerializableProjectFile1.MAGIC)) { // File is a match for SerializableProjectFile1. Strip header and deserialize. serializedData = serializedData.Substring(SerializableProjectFile1.MAGIC.Length); try { bool ok = SerializableProjectFile1.DeserializeProject(serializedData, proj, report); if (ok) { proj.UpdateCpuDef(); } return(ok); } catch (Exception ex) { // Ideally this won't happen -- errors should be caught explicitly. This // is a catch-all to keep us from crashing on expectedly bad input. report.Add(FileLoadItem.Type.Error, Res.Strings.ERR_PROJECT_FILE_CORRUPT + ": " + ex); return(false); } } else { report.Add(FileLoadItem.Type.Error, Res.Strings.ERR_NOT_PROJECT_FILE); return(false); } }
/// <summary> /// Serializes a DisasmProject into an augmented JSON string. /// </summary> /// <param name="proj">Project to serialize.</param> /// <returns>Augmented JSON string.</returns> public static string SerializeProject(DisasmProject proj) { StringBuilder sb = new StringBuilder(); sb.Append(MAGIC); // augment with version string, which will be stripped sb.Append("\r\n"); // will be ignored by deserializer; might get converted to \n SerializableProjectFile1 spf = new SerializableProjectFile1(); spf._ContentVersion = ProjectFile.CONTENT_VERSION; Debug.Assert(proj.FileDataLength == proj.FileData.Length); spf.FileDataLength = proj.FileDataLength; spf.FileDataCrc32 = (int)proj.FileDataCrc32; // Convert AddressMap to serializable form. spf.AddressMap = new List <SerAddressMap>(); foreach (AddressMap.AddressMapEntry ent in proj.AddrMap) { spf.AddressMap.Add(new SerAddressMap(ent)); } // Reduce TypeHints to a collection of ranges. Output the type enum as a string // so we're not tied to a specific value. spf.TypeHints = new List <SerTypeHintRange>(); TypedRangeSet trs = new TypedRangeSet(); for (int i = 0; i < proj.TypeHints.Length; i++) { trs.Add(i, (int)proj.TypeHints[i]); } IEnumerator <TypedRangeSet.TypedRange> iter = trs.RangeListIterator; while (iter.MoveNext()) { if (iter.Current.Type == (int)CodeAnalysis.TypeHint.NoHint) { continue; } spf.TypeHints.Add(new SerTypeHintRange(iter.Current.Low, iter.Current.High, ((CodeAnalysis.TypeHint)iter.Current.Type).ToString())); } // Convert StatusFlagOverrides to serializable form. Just write the state out // as an integer... not expecting it to change. If it does, we can convert. spf.StatusFlagOverrides = new Dictionary <string, int>(); for (int i = 0; i < proj.StatusFlagOverrides.Length; i++) { if (proj.StatusFlagOverrides[i] == Asm65.StatusFlags.DefaultValue) { continue; } spf.StatusFlagOverrides.Add(i.ToString(), proj.StatusFlagOverrides[i].AsInt); } // Convert Comments to serializable form. spf.Comments = new Dictionary <string, string>(); for (int i = 0; i < proj.Comments.Length; i++) { if (string.IsNullOrEmpty(proj.Comments[i])) { continue; } spf.Comments.Add(i.ToString(), proj.Comments[i]); } // Convert multi-line comments to serializable form. spf.LongComments = new Dictionary <string, SerMultiLineComment>(); foreach (KeyValuePair <int, MultiLineComment> kvp in proj.LongComments) { spf.LongComments.Add(kvp.Key.ToString(), new SerMultiLineComment(kvp.Value)); } // Convert multi-line notes to serializable form. spf.Notes = new Dictionary <string, SerMultiLineComment>(); foreach (KeyValuePair <int, MultiLineComment> kvp in proj.Notes) { spf.Notes.Add(kvp.Key.ToString(), new SerMultiLineComment(kvp.Value)); } // Convert user-defined labels to serializable form. spf.UserLabels = new Dictionary <string, SerSymbol>(); foreach (KeyValuePair <int, Symbol> kvp in proj.UserLabels) { spf.UserLabels.Add(kvp.Key.ToString(), new SerSymbol(kvp.Value)); } // Convert operand and data item format descriptors to serializable form. spf.OperandFormats = new Dictionary <string, SerFormatDescriptor>(); foreach (KeyValuePair <int, FormatDescriptor> kvp in proj.OperandFormats) { spf.OperandFormats.Add(kvp.Key.ToString(), new SerFormatDescriptor(kvp.Value)); } spf.ProjectProps = new SerProjectProperties(proj.ProjectProps); JavaScriptSerializer ser = new JavaScriptSerializer(); string cereal = ser.Serialize(spf); sb.Append(cereal); // Stick a linefeed at the end. Makes git happier. sb.Append("\r\n"); return(sb.ToString()); }