public WZDataDirectoryCollection(string path, WZVariant variant, bool encrypted) { _files = Directory.GetFiles(path, "*.nx").ToImmutableDictionary( Path.GetFileNameWithoutExtension, d => new WZFile(d, variant, encrypted) ); }
/// <summary>Creates and loads a WZ file from a path. The Stream created will be disposed when the WZ file is disposed.</summary> /// <param name="path"> The path where the WZ file is located. </param> /// <param name="variant"> The variant of this WZ file. </param> /// <param name="encrypted"> Whether the WZ file is encrypted outside a WZ image. </param> /// <param name="flag"> WZ parsing flags. </param> public unsafe WZFile(string path, WZVariant variant, bool encrypted, WZReadSelection flag = WZReadSelection.None) { _variant = variant; _encrypted = encrypted; _flag = flag; _aes = new WZAES(_variant); _bpo = new MemoryMappedFile(path); _r = new WZBinaryReader(_bpo.Pointer, _bpo.Length, _aes, 0); Parse(); }
private static byte[] GetWZKey(WZVariant version) { switch ((int)version) { case 0: return(GenerateKey(KMSIV, AESKey)); case 1: return(GenerateKey(GMSIV, AESKey)); case 2: return(new byte[0x10000]); default: throw new ArgumentException("Invalid WZ variant passed.", "version"); } }
private static byte[] GetWZKey(WZVariant version, int length) { length = (length & ~15) + ((length & 15) > 0 ? 16 : 0); switch ((int)version) { case 0: return(GenerateKey(KMSIV, AESKey, length)); case 1: return(GenerateKey(GMSIV, AESKey, length)); case 2: return(new byte[length]); default: throw new ArgumentException("Invalid WZ variant passed.", nameof(version)); } }
private static void Main(string[] args) { #region Option parsing string inWz = null, outPath = null; WZVariant wzVar = (WZVariant)255; bool initialEnc = true; OptionSet oSet = new OptionSet { { "in=", "Path to input WZ; required.", a => inWz = a }, { "out=", "Path to output NX; optional, defaults to <WZ file name>.nx in this directory", a => outPath = a }, { "wzv=", "WZ encryption key; required.", a => wzVar = (WZVariant)Enum.Parse(typeof(WZVariant), a, true) }, { "Ds|dumpsound", "Set to include sound properties in the NX file.", a => dumpSnd = true }, { "Di|dumpimage", "Set to include canvas properties in the NX file.", a => dumpImg = true }, { "wzn", "Set if the input WZ is not encrypted.", a => initialEnc = false } }; oSet.Parse(args); if (inWz == null || wzVar == (WZVariant)255) { oSet.WriteOptionDescriptions(Console.Out); return; } if (outPath == null) { outPath = Path.GetFileNameWithoutExtension(inWz) + ".nx"; } #endregion try { Run(inWz, outPath, wzVar, initialEnc); } catch (Exception e) { Console.WriteLine(e); Console.WriteLine("Exception; toggling /wzn and retrying."); initialEnc = !initialEnc; Run(inWz, outPath, wzVar, initialEnc); } }
internal WZAES(WZVariant version) { _wzKey = GetWZKey(version); _asciiKey = new byte[_wzKey.Length]; _unicodeKey = new byte[_wzKey.Length]; _asciiEncKey = new byte[_wzKey.Length]; _unicodeEncKey = new byte[_wzKey.Length]; unchecked { byte mask = 0xAA; for (int i = 0; i < _wzKey.Length; ++i, ++mask) { _asciiKey[i] = mask; _asciiEncKey[i] = (byte)(_wzKey[i] ^ mask); } ushort umask = 0xAAAA; for (int i = 0; i < _wzKey.Length / 2; i += 2, ++umask) { _unicodeKey[i] = (byte)(umask & 0xFF); _unicodeKey[i + 1] = (byte)((umask & 0xFF00) >> 8); _unicodeEncKey[i] = (byte)(_wzKey[i] ^ _unicodeKey[i]); _unicodeEncKey[i + 1] = (byte)(_wzKey[i + 1] ^ _unicodeKey[i + 1]); } } }
internal WZAES(WZVariant version) { _version = version; GenerateKeys(0x10000); }
private static void Run(string inWz, string outPath, WZVariant wzVar, bool initialEnc) { Console.WriteLine("Input .wz: {0}{1}Output .nx: {2}", Path.GetFullPath(inWz), Environment.NewLine, Path.GetFullPath(outPath)); Stopwatch swOperation = new Stopwatch(); Stopwatch fullTimer = new Stopwatch(); Action<string> reportDone = str => { Console.WriteLine("done. E{0} T{1}", swOperation.Elapsed, fullTimer.Elapsed); swOperation.Restart(); Console.Write(str); }; fullTimer.Start(); swOperation.Start(); Console.Write("Parsing input WZ... ".PadRight(31)); WZReadSelection rFlags = WZReadSelection.EagerParseImage | WZReadSelection.EagerParseStrings; if (!dumpImg) rFlags |= WZReadSelection.NeverParseCanvas; using (WZFile wzf = new WZFile(inWz, wzVar, initialEnc, rFlags)) using ( FileStream outFs = new FileStream(outPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) using (BinaryWriter bw = new BinaryWriter(outFs)) { DumpState state = new DumpState(); reportDone("Writing header... ".PadRight(31)); bw.Write(PKG4); bw.Write(new byte[(4 + 8)*4]); reportDone("Writing nodes... ".PadRight(31)); outFs.EnsureMultiple(4); ulong nodeOffset = (ulong) bw.BaseStream.Position; List<WZObject> nodeLevel = new List<WZObject> {wzf.MainDirectory}; while (nodeLevel.Count > 0) WriteNodeLevel(ref nodeLevel, state, bw); ulong stringOffset; uint stringCount = (uint) state.Strings.Count; { reportDone("Writing string data...".PadRight(31)); Dictionary<uint, string> strings = state.Strings.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); ulong[] offsets = new ulong[stringCount]; for (uint idx = 0; idx < stringCount; ++idx) { outFs.EnsureMultiple(2); offsets[idx] = (ulong) bw.BaseStream.Position; WriteString(strings[idx], bw); } outFs.EnsureMultiple(8); stringOffset = (ulong) bw.BaseStream.Position; for (uint idx = 0; idx < stringCount; ++idx) bw.Write(offsets[idx]); } ulong bitmapOffset = 0UL; uint bitmapCount = 0U; if (dumpImg) { reportDone("Writing canvas data...".PadRight(31)); bitmapCount = (uint) state.Canvases.Count; ulong[] offsets = new ulong[bitmapCount]; long cId = 0; foreach (WZCanvasProperty cNode in state.Canvases) { outFs.EnsureMultiple(8); offsets[cId++] = (ulong) bw.BaseStream.Position; WriteBitmap(cNode, bw); } outFs.EnsureMultiple(8); bitmapOffset = (ulong) bw.BaseStream.Position; for (uint idx = 0; idx < bitmapCount; ++idx) bw.Write(offsets[idx]); } ulong soundOffset = 0UL; uint soundCount = 0U; if (dumpSnd) { reportDone("Writing MP3 data... ".PadRight(31)); soundCount = (uint) state.MP3s.Count; ulong[] offsets = new ulong[soundCount]; long cId = 0; foreach (WZAudioProperty mNode in state.MP3s) { outFs.EnsureMultiple(8); offsets[cId++] = (ulong) bw.BaseStream.Position; WriteMP3(mNode, bw); } outFs.EnsureMultiple(8); soundOffset = (ulong) bw.BaseStream.Position; for (uint idx = 0; idx < soundCount; ++idx) bw.Write(offsets[idx]); } reportDone("Writing linked node data... ".PadRight(31)); byte[] uolReplace = new byte[16]; foreach (KeyValuePair<WZUOLProperty, Action<BinaryWriter, byte[]>> pair in state.UOLs) { WZObject result = pair.Key.FinalTarget; if (result == null) continue; bw.BaseStream.Position = (long) (nodeOffset + state.GetNodeID(result)*20 + 4); bw.BaseStream.Read(uolReplace, 0, 16); pair.Value(bw, uolReplace); } reportDone("Finalising... ".PadRight(31)); bw.Seek(4, SeekOrigin.Begin); bw.Write((uint) state.Nodes.Count); bw.Write(nodeOffset); bw.Write(stringCount); bw.Write(stringOffset); bw.Write(bitmapCount); bw.Write(bitmapOffset); bw.Write(soundCount); bw.Write(soundOffset); reportDone("Completed!"); } }
private static void MainMain(string[] args) { #region getopt bool printHelp = false; string aWzInPath = null; bool aWzNamesEnc = true; bool aPngOutput = false; WZVariant aWzVer = (WZVariant)int.MinValue; string aOutputPath = null; Color aBgColor = Color.Black; LoopType aLoop = LoopType.NoLoop; int aPadding = 10; // input, input-wzfile, input-wzpath, input-wzver, output, output-path, background-color, padding OptionSet set = new OptionSet(); set.Add("iwzp=|input-wzpath=", "The path of the animation or image. Required", s => aWzInPath = s); set.Add("iwzv=|input-wzver=", "The WZ key to use when decoding the WZ. Required", s => aWzVer = (WZVariant)Enum.Parse(typeof(WZVariant), s)); set.Add("iwzne|input-wz-names-not-encrypted", "Flag if WZ image names are not encrypted. ", s => aWzNamesEnc = false); set.Add("o=|of=|output-format=", "The method of output: (a)png or gif", s => { switch (s.ToLower()) { case "png": aPngOutput = true; break; case "gif": aPngOutput = false; break; default: throw new ArgumentException("output must be either png or gif"); } }); set.Add("op=|output-path=", "The path to write the output, (A)PNG or GIF, to", s => aOutputPath = s); set.Add("abg=|a-background-color=", "The background color of the animated output. Default is black. Ignored if there is no animation.", s => aBgColor = Color.FromArgb(int.Parse(s))); set.Add("ap=|a-padding=", "The amount of padding in pixels to pad the animated output with. Default is 10. Ignored if there is no animation.", s => aPadding = int.Parse(s)); set.Add("al=|a-looping=", "The method to loop multi-animations with. Default is NoLoop. Ignored if there is no animation.", s => aLoop = (LoopType)Enum.Parse(typeof(LoopType), s, true)); set.Add("?|h|help", "Shows help", s => printHelp = true); set.Parse(args); #endregion #region check params printHelp |= aWzInPath == null || aWzVer == (WZVariant)int.MinValue || aOutputPath == null; if (printHelp) { PrintHelp(set); return; } #endregion string[] wzpaths = aWzInPath.Split('*'); List <List <Frame> > framess = new List <List <Frame> >(); string newPath = null, cWzPath = null; WZFile wz = null; foreach (string[] split in wzpaths.Select(wzpath => wzpath.Split('?'))) { string inPath; if (newPath != null && split.Length == 1) { inPath = split[0]; } else { newPath = split[0]; inPath = split[1]; } if (cWzPath != newPath && wz != null) { wz.Dispose(); } #region wz parsing if (cWzPath != newPath) { wz = new WZFile(newPath, aWzVer, aWzNamesEnc); } cWzPath = newPath; #endregion #region getting single image WZCanvasProperty wzcp = wz.ResolvePath(inPath).ResolveUOL() as WZCanvasProperty; if (wzcp != null) { Bitmap b = wzcp.Value; if (aPngOutput) { OutputMethods.OutputPNG(b, aOutputPath); } else { OutputMethods.OutputGIF(b, aOutputPath); } return; } #endregion try { List <Frame> data = InputMethods.InputWz(wz, inPath); if (data.Count > 0) { framess.Add(data); } } catch (Exception e) { Console.WriteLine("An error occured while retrieving frames. Check your arguments."); Console.WriteLine(e); throw; } } IEnumerable <Frame> final = OffsetAnimator.Process(new Rectangle(aPadding, aPadding, aPadding, aPadding), aBgColor, aLoop, framess.ToArray()); framess.ForEach(f => f.ForEach(g => g.Image.Dispose())); if (aPngOutput) #if APNG { OutputMethods.OutputAPNG(final, aOutputPath); } else #else { Console.WriteLine("APNG output not supported in this version; outputting GIF."); } #endif { OutputMethods.OutputAGIF(final, aOutputPath); } }
private static byte[] GetWZKey(WZVariant version, int length) { length = (length & ~15) + ((length & 15) > 0 ? 16 : 0); switch ((int) version) { case 0: return GenerateKey(KMSIV, AESKey, length); case 1: return GenerateKey(GMSIV, AESKey, length); case 2: return new byte[length]; default: throw new ArgumentException("Invalid WZ variant passed.", nameof(version)); } }
private static void Run(string inWz, string outPath, WZVariant wzVar, bool initialEnc) { Console.WriteLine("Input .wz: {0}{1}Output .nx: {2}", Path.GetFullPath(inWz), Environment.NewLine, Path.GetFullPath(outPath)); Stopwatch swOperation = new Stopwatch(); Stopwatch fullTimer = new Stopwatch(); Action <string> reportDone = str => { Console.WriteLine("done. E{0} T{1}", swOperation.Elapsed, fullTimer.Elapsed); swOperation.Restart(); Console.Write(str); }; fullTimer.Start(); swOperation.Start(); Console.Write("Parsing input WZ... ".PadRight(31)); WZReadSelection rFlags = WZReadSelection.EagerParseImage | WZReadSelection.EagerParseStrings; if (!dumpImg) { rFlags |= WZReadSelection.NeverParseCanvas; } using (WZFile wzf = new WZFile(inWz, wzVar, initialEnc, rFlags)) using ( FileStream outFs = new FileStream(outPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) using (BinaryWriter bw = new BinaryWriter(outFs)) { DumpState state = new DumpState(); reportDone("Writing header... ".PadRight(31)); bw.Write(PKG4); bw.Write(new byte[(4 + 8) * 4]); reportDone("Writing nodes... ".PadRight(31)); outFs.EnsureMultiple(4); ulong nodeOffset = (ulong)bw.BaseStream.Position; List <WZObject> nodeLevel = new List <WZObject> { wzf.MainDirectory }; while (nodeLevel.Count > 0) { WriteNodeLevel(ref nodeLevel, state, bw); } ulong stringOffset; uint stringCount = (uint)state.Strings.Count; { reportDone("Writing string data...".PadRight(31)); Dictionary <uint, string> strings = state.Strings.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); ulong[] offsets = new ulong[stringCount]; for (uint idx = 0; idx < stringCount; ++idx) { outFs.EnsureMultiple(2); offsets[idx] = (ulong)bw.BaseStream.Position; WriteString(strings[idx], bw); } outFs.EnsureMultiple(8); stringOffset = (ulong)bw.BaseStream.Position; for (uint idx = 0; idx < stringCount; ++idx) { bw.Write(offsets[idx]); } } ulong bitmapOffset = 0UL; uint bitmapCount = 0U; if (dumpImg) { reportDone("Writing canvas data...".PadRight(31)); bitmapCount = (uint)state.Canvases.Count; ulong[] offsets = new ulong[bitmapCount]; long cId = 0; foreach (WZCanvasProperty cNode in state.Canvases) { outFs.EnsureMultiple(8); offsets[cId++] = (ulong)bw.BaseStream.Position; WriteBitmap(cNode, bw); } outFs.EnsureMultiple(8); bitmapOffset = (ulong)bw.BaseStream.Position; for (uint idx = 0; idx < bitmapCount; ++idx) { bw.Write(offsets[idx]); } } ulong soundOffset = 0UL; uint soundCount = 0U; if (dumpSnd) { reportDone("Writing MP3 data... ".PadRight(31)); soundCount = (uint)state.MP3s.Count; ulong[] offsets = new ulong[soundCount]; long cId = 0; foreach (WZAudioProperty mNode in state.MP3s) { outFs.EnsureMultiple(8); offsets[cId++] = (ulong)bw.BaseStream.Position; WriteMP3(mNode, bw); } outFs.EnsureMultiple(8); soundOffset = (ulong)bw.BaseStream.Position; for (uint idx = 0; idx < soundCount; ++idx) { bw.Write(offsets[idx]); } } reportDone("Writing linked node data... ".PadRight(31)); byte[] uolReplace = new byte[16]; foreach (KeyValuePair <WZUOLProperty, Action <BinaryWriter, byte[]> > pair in state.UOLs) { WZObject result = pair.Key.FinalTarget; if (result == null) { continue; } bw.BaseStream.Position = (long)(nodeOffset + state.GetNodeID(result) * 20 + 4); bw.BaseStream.Read(uolReplace, 0, 16); pair.Value(bw, uolReplace); } reportDone("Finalising... ".PadRight(31)); bw.Seek(4, SeekOrigin.Begin); bw.Write((uint)state.Nodes.Count); bw.Write(nodeOffset); bw.Write(stringCount); bw.Write(stringOffset); bw.Write(bitmapCount); bw.Write(bitmapOffset); bw.Write(soundCount); bw.Write(soundOffset); reportDone("Completed!"); } }
public static void AddWZParser(this IServiceCollection c, string directory, WZVariant variant, bool encrypted) { c.AddSingleton <IDataDirectoryCollection>(new WZDataDirectoryCollection(directory, variant, encrypted)); }