public static ObjectData Decode(byte[] bytes) { if (bytes == null) { return(null); } bytes = LZMACoder.Uncompress(bytes); using (BinaryReader reader = new BinaryReader(new MemoryStream(bytes))) { ushort version = reader.ReadUInt16(); if (version == (ushort)ObdVersion.Version3) { return(DecodeV3(reader)); } else if (version == (ushort)ObdVersion.Version2) { return(DecodeV2(reader)); } else if (version >= (ushort)DatFormat.Format_710) { return(DecodeV1(reader)); } else { //// } } return(null); }
/// <summary> /// Writes the current replay to a binary file. /// </summary> public void Write(string path) { var frames = FramesToString(); ReplayVersion = CurrentVersion; using (var replayDataStream = new MemoryStream(Encoding.ASCII.GetBytes(frames))) using (var bw = new BinaryWriter(File.Open(path, FileMode.Create))) { bw.Write(ReplayVersion); bw.Write(MapMd5); bw.Write(GetMd5(frames)); bw.Write(PlayerName); bw.Write(DateTime.Now.ToString(CultureInfo.InvariantCulture)); bw.Write(TimePlayed); bw.Write((int)Mode); bw.Write((int)Mods); bw.Write(Score); bw.Write(Accuracy); bw.Write(MaxCombo); bw.Write(CountMarv); bw.Write(CountPerf); bw.Write(CountGreat); bw.Write(CountGood); bw.Write(CountOkay); bw.Write(CountMiss); bw.Write(PauseCount); bw.Write(RandomizeModifierSeed); bw.Write(StreamHelper.ConvertStreamToByteArray(LZMACoder.Compress(replayDataStream))); } }
public static byte[] Decompress(byte[] inputBytes) { byte[] result; using (LZMACoder coder = new LZMACoder()) using (MemoryStream input = new MemoryStream(inputBytes)) using (MemoryStream output = new MemoryStream()) { coder.Decompress(input, output); result = output.ToArray(); } return(result); }
/// <summary> /// Writes the current replay to a binary file. /// </summary> public void Write(string path) { var frames = FramesToString(); // TOOD: This should be removed when everyone is running the new redesign client // This will manually downgrade the replay version if the user isn't using new modifiers to keep // compatibility between old and new clients. // 0.0.1 used to write the modifiers as a 32-bit integer, but because of the amount of new mods, they need // to be written as 64-bit. ReplayVersion = Mods < ModIdentifier.Speed105X ? "0.0.1" : CurrentVersion; using (var replayDataStream = new MemoryStream(Encoding.ASCII.GetBytes(frames))) using (var bw = new BinaryWriter(File.Open(path, FileMode.Create))) { bw.Write(ReplayVersion); bw.Write(MapMd5); bw.Write(GetMd5(frames)); bw.Write(PlayerName); bw.Write(DateTime.Now.ToString(CultureInfo.InvariantCulture)); bw.Write(TimePlayed); bw.Write((int)Mode); // This check is to keep compatibility with older clients. // We only want to write a 64-bit integer for replays with newer mods activated if (ReplayVersion == "0.0.1" || Mods < ModIdentifier.Speed105X) { bw.Write((int)Mods); } else { bw.Write((long)Mods); } bw.Write(Score); bw.Write(Accuracy); bw.Write(MaxCombo); bw.Write(CountMarv); bw.Write(CountPerf); bw.Write(CountGreat); bw.Write(CountGood); bw.Write(CountOkay); bw.Write(CountMiss); bw.Write(PauseCount); bw.Write(RandomizeModifierSeed); bw.Write(StreamHelper.ConvertStreamToByteArray(LZMACoder.Compress(replayDataStream))); } }
private bool Download(CacheContentFile file) { var fileName = Path.Combine(Location, file.Name); var outputDirectory = Path.GetDirectoryName(fileName); var temporaryFile = Path.Combine(outputDirectory, file.SHA1Hash + ".cpart"); var URL = _server + file.Cache + "/" + file.Name; if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } int startAt = 0; Log.Info(String.Format("Starting to download {0}, {1} bytes.", file.Name, file.Size)); if (File.Exists(temporaryFile)) { //resume the download if (_usecpart) { var fi = new FileInfo(temporaryFile); startAt = (int)fi.Length; SendStatus(StatusChangedEnum.FileDownloading, new object[] { file.Name, file.CompressedSize, startAt }, startAt); Log.Info(String.Format("Partial file for {1} found, starting download at {0} bytes.", startAt, file.Name)); } else { File.Delete(temporaryFile); } } bool compressed = false; #if !DEBUG try { #endif if (Utilities.URLExists(URL + ".lzma")) { compressed = true; URL += ".lzma"; SendStatus(StatusChangedEnum.FileDownloadingLzma, file.Name, 0); Log.Info(String.Format("Found a compressed file for {0}.", fileName)); } Stream inStream, outStream; if (startAt != file.Size && startAt != file.CompressedSize) //if they equal, ._. { // initialize request var webRequest = (HttpWebRequest)WebRequest.Create(URL); webRequest.AddRange(startAt); var webResponse = (HttpWebResponse)webRequest.GetResponse(); inStream = webResponse.GetResponseStream(); outStream = File.Open(temporaryFile, FileMode.Append, FileAccess.Write, FileShare.Read); var buffer = new byte[2048]; var lastBytes = 0; while ((lastBytes = inStream.Read(buffer, 0, buffer.Length)) > 0) { outStream.Write(buffer, 0, lastBytes); SendStatus(StatusChangedEnum.FileDownloading, new object[] { file.Name, file.CompressedSize, lastBytes }, lastBytes); } // clean up the streams inStream.Close(); outStream.Close(); SendStatus(StatusChangedEnum.FileDownloadFinish, file.Name, 0); } if (compressed) { try { // decompress the temp file var tempDecPath = fileName + ".deco"; var decoder = new LZMACoder(); SendStatus(StatusChangedEnum.FileDecompressStart, file.Name, 0); Log.Info("decompressing " + file.Name); inStream = File.OpenRead(temporaryFile); outStream = File.Open(tempDecPath, FileMode.Create); try { decoder.Decompress(inStream, outStream); } catch (ApplicationException ex) { inStream.Close(); outStream.Close(); throw ex; } inStream.Close(); outStream.Close(); File.Delete(temporaryFile); File.Move(tempDecPath, temporaryFile); SendStatus(StatusChangedEnum.FileDecompressFinish, file.Name, 0); } catch (ApplicationException e) { // lzma sdk threw an error. File.Delete(temporaryFile); Log.Error(String.Format("LZMA file failed to decompress. {0}", e.ToString())); _lastException = e; return(false); } } // check the temp file's validity SendStatus(StatusChangedEnum.FileVerifyStart, file.Name, 0); var outHash = Utilities.GetFileSHA1(temporaryFile); var inHash = file.SHA1Hash; if (inHash != outHash) { _lastException = new Exception("Hash verification failed for " + fileName); Log.Error("Hash verification failed for " + fileName); File.Delete(temporaryFile); SendStatus(StatusChangedEnum.FileVerifyFailed, file.Name, 0); return(false); } SendStatus(StatusChangedEnum.FileVerifyFinish, file.Name, 0); // and rename the temp file, and then delete it File.Copy(temporaryFile, fileName, true); File.Delete(temporaryFile); return(true); #if !DEBUG } catch (Exception e) { Log.Error(String.Format("Error while downloading {0}.", e.ToString())); _lastException = e; } return(false); #endif }
private static byte[] EncodeV1(ObjectData data) { using (FlagsBinaryWriter writer = new FlagsBinaryWriter(new MemoryStream())) { // write client version writer.Write((ushort)DatFormat.Format_1010); // write category string category = string.Empty; switch (data.Category) { case ThingCategory.Item: category = "item"; break; case ThingCategory.Outfit: category = "outfit"; break; case ThingCategory.Effect: category = "effect"; break; case ThingCategory.Missile: category = "missile"; break; } writer.Write((ushort)category.Length); writer.Write(Encoding.UTF8.GetBytes(category)); if (!ThingTypeSerializer.WriteProperties(data.ThingType, DatFormat.Format_1010, writer)) { return(null); } FrameGroup group = data.GetFrameGroup(FrameGroupType.Default); writer.Write(group.Width); writer.Write(group.Height); if (group.Width > 1 || group.Height > 1) { writer.Write(group.ExactSize); } writer.Write(group.Layers); writer.Write(group.PatternX); writer.Write(group.PatternY); writer.Write(group.PatternZ); writer.Write(group.Frames); Sprite[] sprites = data.Sprites[FrameGroupType.Default]; for (int i = 0; i < sprites.Length; i++) { Sprite sprite = sprites[i]; byte[] pixels = sprite.GetARGBPixels(); writer.Write((uint)sprite.ID); writer.Write((uint)pixels.Length); writer.Write(pixels); } return(LZMACoder.Compress(((MemoryStream)writer.BaseStream).ToArray())); } }
private static byte[] EncodeV2(ObjectData data) { using (FlagsBinaryWriter writer = new FlagsBinaryWriter(new MemoryStream())) { // write obd version writer.Write((ushort)ObdVersion.Version2); // write client version writer.Write((ushort)DatFormat.Format_1050); // write category writer.Write((byte)data.Category); // skipping the texture patterns position. int patternsPosition = (int)writer.BaseStream.Position; writer.Seek(4, SeekOrigin.Current); if (!WriteProperties(data.ThingType, writer)) { return(null); } // write the texture patterns position. int position = (int)writer.BaseStream.Position; writer.Seek(patternsPosition, SeekOrigin.Begin); writer.Write((uint)writer.BaseStream.Position); writer.Seek(position, SeekOrigin.Begin); FrameGroup group = data.GetFrameGroup(FrameGroupType.Default); writer.Write(group.Width); writer.Write(group.Height); if (group.Width > 1 || group.Height > 1) { writer.Write(group.ExactSize); } writer.Write(group.Layers); writer.Write(group.PatternX); writer.Write(group.PatternY); writer.Write(group.PatternZ); writer.Write(group.Frames); if (group.IsAnimation) { writer.Write((byte)group.AnimationMode); writer.Write(group.LoopCount); writer.Write(group.StartFrame); for (int i = 0; i < group.Frames; i++) { writer.Write((uint)group.FrameDurations[i].Minimum); writer.Write((uint)group.FrameDurations[i].Maximum); } } Sprite[] sprites = data.Sprites[FrameGroupType.Default]; for (int i = 0; i < sprites.Length; i++) { Sprite sprite = sprites[i]; byte[] pixels = sprite.GetARGBPixels(); writer.Write(sprite.ID); writer.Write(pixels); } return(LZMACoder.Compress(((MemoryStream)writer.BaseStream).ToArray())); } }
/// <summary> /// Ctor - Read replay. /// </summary> /// <param name="path"></param> /// <param name="readHeaderless"></param> public Replay(string path, bool readHeaderless = false) { if (!File.Exists(path)) { throw new FileNotFoundException(); } // Read the replay data using (var fs = new FileStream(path, FileMode.Open)) using (var br = new BinaryReader(fs)) { if (!readHeaderless) { // Version None (Original data) ReplayVersion = br.ReadString(); MapMd5 = br.ReadString(); Md5 = br.ReadString(); PlayerName = br.ReadString(); Date = Convert.ToDateTime(br.ReadString(), CultureInfo.InvariantCulture); TimePlayed = br.ReadInt64(); // The dates are serialized incorrectly in older replays, so to keep compatibility, // use the time played. Date = new DateTime(1970, 1, 1, 0, 0, 0, 0).AddMilliseconds(TimePlayed); Mode = (GameMode)br.ReadInt32(); // The mirror mod is the 32-nd bit, which, when read as an Int32, results in a negative number. // To fix this, that case is handled separately. if (ReplayVersion == "0.0.1" || ReplayVersion == "None") { var mods = br.ReadInt32(); // First check if the mods are None, which is -1 (all bits set). This doesn't cause issues because // there are incompatible mods among the first 32 (like all the speed mods), so all first 32 bits // can't be 1 simultaneously unless it's a None. if (mods == -1) { Mods = ModIdentifier.None; mods = 0; } else if (mods < 0) { // If the 32-nd bit is set, add the Mirror mod and unset the 32-nd bit. Mods = ModIdentifier.Mirror; mods &= ~(1 << 31); } // Add the rest of the mods. Mods |= (ModIdentifier)mods; } else { Mods = (ModIdentifier)br.ReadInt64(); } Score = br.ReadInt32(); Accuracy = br.ReadSingle(); MaxCombo = br.ReadInt32(); CountMarv = br.ReadInt32(); CountPerf = br.ReadInt32(); CountGreat = br.ReadInt32(); CountGood = br.ReadInt32(); CountOkay = br.ReadInt32(); CountMiss = br.ReadInt32(); PauseCount = br.ReadInt32(); // Versions beyond None if (ReplayVersion != "None") { var replayVersion = new Version(ReplayVersion); if (replayVersion >= new Version("0.0.1")) { RandomizeModifierSeed = br.ReadInt32(); } } } // Create the new list of replay frames. Frames = new List <ReplayFrame>(); // Split the frames up by commas var frames = new List <string>(); if (!readHeaderless) { frames = Encoding.ASCII.GetString(LZMACoder.Decompress(br.BaseStream).ToArray()).Split(',').ToList(); } else { frames = Encoding.ASCII.GetString(LZMACoder.Decompress(br.ReadBytes((int)br.BaseStream.Length))).Split(',').ToList(); } // Add all the replay frames to the object foreach (var frame in frames) { try { // Split up the frame string by SongTime|KeyPressState var frameSplit = frame.Split('|'); Frames.Add(new ReplayFrame(int.Parse(frameSplit[0]), (ReplayKeyPressState)Enum.Parse(typeof(ReplayKeyPressState), frameSplit[1]))); } catch (Exception e) { continue; } } } }
/// <summary> /// Ctor - Read replay. /// </summary> /// <param name="path"></param> /// <param name="readHeaderless"></param> public Replay(string path, bool readHeaderless = false) { if (!File.Exists(path)) { throw new FileNotFoundException(); } // Read the replay data using (var fs = new FileStream(path, FileMode.Open)) using (var br = new BinaryReader(fs)) { if (!readHeaderless) { // Version None (Original data) ReplayVersion = br.ReadString(); MapMd5 = br.ReadString(); Md5 = br.ReadString(); PlayerName = br.ReadString(); Date = Convert.ToDateTime(br.ReadString(), CultureInfo.InvariantCulture); TimePlayed = br.ReadInt64(); // The dates are serialized incorrectly in older replays, so to keep compatibility, // use the time played. Date = new DateTime(1970, 1, 1, 0, 0, 0, 0).AddMilliseconds(TimePlayed); Mode = (GameMode)br.ReadInt32(); Mods = (ModIdentifier)br.ReadInt32(); Score = br.ReadInt32(); Accuracy = br.ReadSingle(); MaxCombo = br.ReadInt32(); CountMarv = br.ReadInt32(); CountPerf = br.ReadInt32(); CountGreat = br.ReadInt32(); CountGood = br.ReadInt32(); CountOkay = br.ReadInt32(); CountMiss = br.ReadInt32(); PauseCount = br.ReadInt32(); // Versions beyond None if (ReplayVersion != "None") { var replayVersion = new Version(ReplayVersion); if (replayVersion >= new Version("0.0.1")) { RandomizeModifierSeed = br.ReadInt32(); } } } // Create the new list of replay frames. Frames = new List <ReplayFrame>(); // Split the frames up by commas var frames = new List <string>(); if (!readHeaderless) { frames = Encoding.ASCII.GetString(LZMACoder.Decompress(br.BaseStream).ToArray()).Split(',').ToList(); } else { frames = Encoding.ASCII.GetString(LZMACoder.Decompress(br.ReadBytes((int)br.BaseStream.Length))).Split(',').ToList(); } // Add all the replay frames to the object foreach (var frame in frames) { try { // Split up the frame string by SongTime|KeyPressState var frameSplit = frame.Split('|'); Frames.Add(new ReplayFrame(int.Parse(frameSplit[0]), (ReplayKeyPressState)Enum.Parse(typeof(ReplayKeyPressState), frameSplit[1]))); } catch (Exception e) { continue; } } } }
static void Main(string[] args) { //some logical steps to help my mind code :> //1. read Caches folder, if a caches.xml file is found, go to step 3, if not, 2 //2. get all the directories inside the Caches folder, add those to a list with version number being 0 //3. get all the caches inside the cache file, add those to a list with its version number //4. read all cache folders recursively, if an info.xml file exists, use that file to verify if any files have changed. //5. any files that haven't changed (or all if an info.xml file doesn't exist) or are new should be compressed and added to the info.xml file //6. build the caches.xml file with an incremented version (if the info.xml file has changed) //7. copy all files to be uploaded (caches.xml + directories with info.xml and only files to be uploaded (lzma's)) to the CacheUpload folder Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var cf = CombinePaths(Environment.CurrentDirectory, "Caches", "caches.xml"); try { Directory.Delete(CombinePaths(Environment.CurrentDirectory, "CachesUpload"), true); } catch (DirectoryNotFoundException) { } var cacheDict = new SortedDictionary <string, int>(); var cacheChanged = new SortedDictionary <string, bool>(); if (File.Exists(cf)) { foreach (var el in XDocument.Parse(File.ReadAllText(cf, Encoding.UTF8)).Element("Caches").Elements()) { cacheDict.Add(el.Attribute("ID").Value, int.Parse(el.Attribute("Version").Value)); } } foreach (var dir in Directory.GetDirectories(Path.Combine(Environment.CurrentDirectory, "Caches"))) { if (!cacheDict.ContainsKey(Path.GetFileName(dir))) { cacheDict.Add(Path.GetFileName(dir), 0); } } foreach (var keyval in cacheDict) { var changed = false; var CCFList = new SortedDictionary <string, CacheContentFile>(); var infoXML = new XDocument(); var currentFolder = CombinePaths(Environment.CurrentDirectory, "Caches", keyval.Key); if (File.Exists(CombinePaths(currentFolder, "info.xml"))) { foreach (var el in XDocument.Parse(File.ReadAllText(CombinePaths(currentFolder, "info.xml"), Encoding.UTF8)).Element("CacheInfo").Elements()) { var name = el.Attribute("Name").Value; if (File.Exists(CombinePaths(currentFolder, name))) { var size = long.Parse(el.Attribute("Size").Value); var sizeCompressed = long.Parse(el.Attribute("CompressedSize").Value); var hash = el.Attribute("SHA1Hash").Value; CCFList.Add(name, new CacheContentFile { SHA1Hash = hash, Size = size, CompressedSize = sizeCompressed }); } } } foreach (var file in GetFilesRecursive(currentFolder)) { var afile = file.Replace(currentFolder, "").Substring(1).Replace("\\", "/"); //remove current folder and the slash, replace \ with / for linux (still works on windows) if (Path.GetFileName(afile) == "info.xml" || Path.GetExtension(afile) == ".lzma") { continue; //both files shouldn't be here!!!1 } var length = new FileInfo(Path.Combine(currentFolder, afile)).Length; var lengthCompressed = length; var hash = GetFileSHA1(Path.Combine(currentFolder, afile)); bool compressed = true; if (!CCFList.ContainsKey(afile) || CCFList[afile].SHA1Hash != hash || CCFList[afile].Size != length) { changed = true; //doesn't exist in info.xml, hash changed or size changed var pathToGo = CombinePaths(Environment.CurrentDirectory, "CacheUpload", keyval.Key, afile); if (length < 75 * 1024 * 1024) //bigger than 75mb = no compress (should work for most situations) { var lz = new LZMACoder(); var inStream = File.Open(Path.Combine(currentFolder, afile), FileMode.Open, FileAccess.Read); Directory.CreateDirectory(Path.GetDirectoryName(pathToGo)); var outStream = File.Open(pathToGo + ".lzma", FileMode.Create, FileAccess.Write); try { Console.WriteLine("Compressing {0} to {1}....", Path.Combine(currentFolder, afile), pathToGo + ".lzma"); lz.Compress(inStream, outStream); inStream.Close(); outStream.Close(); lengthCompressed = new FileInfo(pathToGo + ".lzma").Length; } catch (Exception e) { Console.WriteLine("Failed to compress {0}, {1}", file, e.ToString()); inStream.Close(); outStream.Close(); compressed = false; File.Delete(pathToGo + ".lzma"); } } if (!compressed) { File.Copy(Path.Combine(currentFolder, afile), pathToGo, true); } if (CCFList.ContainsKey(afile)) { CCFList[afile].Size = length; CCFList[afile].CompressedSize = lengthCompressed; CCFList[afile].SHA1Hash = hash; } else { CCFList.Add(afile, new CacheContentFile { Size = length, CompressedSize = lengthCompressed, SHA1Hash = hash }); } } } infoXML = new XDocument( new XElement("CacheInfo") ); foreach (var keyval2 in CCFList) { infoXML.Element("CacheInfo").Add( new XElement("CacheFile", new XAttribute("Name", keyval2.Key), new XAttribute("Size", keyval2.Value.Size.ToString()), new XAttribute("CompressedSize", keyval2.Value.CompressedSize.ToString()), new XAttribute("SHA1Hash", keyval2.Value.SHA1Hash) ) ); } cacheChanged.Add(keyval.Key, changed); // using (XmlTextWriter writer = new XmlTextWriter(Path.Combine(currentFolder, "info.xml"), null)) // { // infoXML.Save(writer); //// } Store(infoXML, Path.Combine(currentFolder, "info.xml")); Directory.CreateDirectory(Path.GetDirectoryName(CombinePaths(Environment.CurrentDirectory, "CacheUpload", keyval.Key, "info.xml"))); File.Copy(Path.Combine(currentFolder, "info.xml"), CombinePaths(Environment.CurrentDirectory, "CacheUpload", keyval.Key, "info.xml"), true); } var caches = new XDocument( new XElement("Caches") ); foreach (var keyval3 in cacheDict) { caches.Element("Caches").Add( new XElement("Cache", new XAttribute("ID", keyval3.Key), new XAttribute("Version", (keyval3.Value + (cacheChanged[keyval3.Key] ? 1 : 0)).ToString()) ) ); } // using (XmlTextWriter writer = new XmlTextWriter(CombinePaths(Environment.CurrentDirectory, "Caches", "caches.xml"), null)) // { // caches.Save(writer); // } Store(caches, CombinePaths(Environment.CurrentDirectory, "Caches", "caches.xml")); Directory.CreateDirectory(Path.GetDirectoryName(CombinePaths(Environment.CurrentDirectory, "Caches", "caches.xml"))); File.Copy(CombinePaths(Environment.CurrentDirectory, "Caches", "caches.xml"), CombinePaths(Environment.CurrentDirectory, "CacheUpload", "caches.xml"), true); }