private Level(BinaryReader reader) : this() { ID = reader.ReadInt32(); var nameLength = reader.ReadInt32(); Name = Encoding.UTF8.GetString(reader.ReadBytes(nameLength)); SPlusTime = reader.ReadUInt16(); STime = reader.ReadUInt16(); ATime = reader.ReadUInt16(); BTime = reader.ReadUInt16(); CTime = reader.ReadUInt16(); CheckTime(); var prismsCount = reader.ReadInt16(); Size = new Size3D(reader); #region Boring verifying var temp = reader.ReadUInt16(); if (Temp1 != temp) { Warning.WriteLine(string.Format(Localization.LevelInvalidArgument, temp, Temp1, "unknown_ushort_1")); } temp = reader.ReadUInt16(); if (Temp2 != temp) { Warning.WriteLine(string.Format(Localization.LevelInvalidArgument, temp, Temp2, "unknown_ushort_2")); } ushort width = reader.ReadUInt16(), length = reader.ReadUInt16(); var legacyMinimapValid = true; if (LegacyMinimapSize.Width != width) { Warning.WriteLine(string.Format(Localization.LevelInvalidArgument, width, LegacyMinimapSize.Width, "unknown_ushort_3")); legacyMinimapValid = false; } if (LegacyMinimapSize.Length != length) { Warning.WriteLine(string.Format(Localization.LevelInvalidArgument, width, LegacyMinimapSize.Length, "unknown_ushort_4")); legacyMinimapValid = false; } if (!legacyMinimapValid) { Warning.WriteLine(Localization.LevelLegacyMinimapMalformed); } var tempByte = reader.ReadByte(); if (tempByte != 10) { Warning.WriteLine(string.Format(Localization.LevelInvalidArgument, temp, 10, "unknown_byte_1")); } temp = reader.ReadUInt16(); if (Size.Length - 1 != temp) { Warning.WriteLine(string.Format (Localization.LevelInvalidArgument, temp, Size.Length - 1, "unknown_ushort_5")); } temp = reader.ReadUInt16(); if (0 != temp) { Warning.WriteLine(string.Format(Localization.LevelInvalidArgument, temp, 0, "unknown_ushort_6")); } #endregion if (legacyMinimapValid) { LegacyMinimap = new Flat(reader, LegacyMinimapSize); } else { LegacyMinimap = new Flat(LegacyMinimapSize); reader.ReadBytes((width * length + 7) / 8); } CollisionMap = new Cube(reader, Size); SpawnPoint = new Point3D16(reader); Zoom = reader.ReadInt16(); if (Zoom < 0) { Value = reader.ReadInt16(); ValueIsAngle = reader.ReadBoolean(); } ExitPoint = new Point3D16(reader); var count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { MovingPlatforms.Add(new MovingPlatform(MovingPlatforms, reader)); } count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { Bumpers.Add(new Bumper(Bumpers, reader)); } count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { FallingPlatforms.Add(new FallingPlatform(this, reader)); } count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { Checkpoints.Add(new Checkpoint(reader)); } count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { CameraTriggers.Add(new CameraTrigger(reader)); } count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { Prisms.Add(new Prism(reader)); } if (count != prismsCount) { Warning.WriteLine(string.Format (Localization.LevelInvalidArgument, prismsCount, count, "prisms_count")); } if ((count = reader.ReadUInt16()) > 0) { Warning.WriteLine(string.Format(Localization.DeprecatedElement, "Fan")); } for (var i = 0; i < count; i++) { Fans.Add(new Fan(reader)); } Buttons = new Buttons(this, reader); count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { OtherCubes.Add(new OtherCube(this, reader)); } count = reader.ReadUInt16(); for (var i = 0; i < count; i++) { Resizers.Add(new Resizer(this, reader)); } if ((count = reader.ReadUInt16()) > 0) { Warning.WriteLine(string.Format(Localization.DeprecatedElement, "MiniBlock")); } for (var i = 0; i < count; i++) { MiniBlocks.Add(new MiniBlock(reader)); } ModelTheme = Theme = reader.ReadByte(); MusicJava = reader.ReadByte(); Music = reader.ReadByte(); foreach (var button in Buttons) { if (button.IsMoving) { button.MovingPlatformID.Name.DoNothing(); } foreach (var e in button.Events) { Buttons.BlockEvents[e].ID.Name.DoNothing(); } } foreach (var cube in OtherCubes) { cube.MovingBlockSync.Name.DoNothing(); cube.DarkCubeMovingBlockSync?.Name.DoNothing(); } }
public OtherCube(Level parent, XElement element) { element.GetAttributeValue(out PositionTrigger, "PositionTrigger"); var id = element.GetAttributeValue("MovingBlockSync"); var sync = id == null ? new IDReference <MovingPlatform>(parent.MovingPlatforms, -1) : new IDReference <MovingPlatform>(parent.MovingPlatforms, id); element.GetAttributeValueWithDefault(out DarkCubeRadius, "Radius"); element.GetAttributeValue(out PositionCube, "PositionCube"); foreach (var e in element.Elements()) { try { KeyEvents.Add(new KeyEvent(e)); } catch { if (!e.Name.LocalName.Equals("MovingPlatform", StringComparison.InvariantCultureIgnoreCase) && !e.Name.LocalName.Equals("Button", StringComparison.InvariantCultureIgnoreCase)) { Warning.WriteLine(string.Format(Localization.UnrecognizedChildElement, e.Name, element.Name)); } } } if (element.Name == "OtherCube") { MovingBlockSync = sync; var mode = element.GetAttributeValueWithDefault("Mode", OtherCubeMode.AutoHide); var moveDirection = element.GetAttributeValueWithDefault("MoveDirection", GetDefaultDirection(mode)); if (mode == OtherCubeMode.Hole) { PositionTrigger -= moveDirection; } AddHelper(parent, mode, PositionTrigger, moveDirection, element); if (DarkCubeRadius.Equals(default(Point2D8))) { return; } var radius = DarkCubeRadius; DarkCubeRadius = default(Point2D8); for (var x = -radius.X; x <= radius.X; x++) { for (var y = -radius.Y; y <= radius.Y; y++) { if (x != 0 || y != 0) { var position = PositionTrigger + new Point3D16((short)x, (short)y, 0); parent.OtherCubes.Add(new OtherCube { PositionTrigger = position, MovingBlockSync = MovingBlockSync, PositionCube = PositionCube, KeyEvents = KeyEvents }); AddHelper(parent, mode, position, moveDirection, element); } } } } else { MovingBlockSync = new IDReference <MovingPlatform>(parent.MovingPlatforms, -2); DarkCubeMovingBlockSync = sync; } }
private Level(string path) : this() { var element = XHelper.Load(path + ".xml").GetElement("Level"); ID = element.GetAttributeValue <int>("ID"); Name = element.GetAttributeValueWithDefault("Name", string.Empty); var thresholds = element.GetAttributeValueWithDefault("TimeThresholds", "1,2,3,4,5").Split(',') .Select(str => ushort.Parse(str.Trim())).ToArray(); SPlusTime = thresholds[0]; STime = thresholds[1]; ATime = thresholds[2]; BTime = thresholds[3]; CTime = thresholds[4]; CheckTime(); Size = element.GetAttributeValue <Size3D>("Size"); LegacyMinimap = new Flat(path + ".png", LegacyMinimapSize); CollisionMap = new Cube(path + ".{0}.png", Size); SpawnPoint = element.GetAttributeValue <Point3D16>("SpawnPoint"); ExitPoint = element.GetAttributeValue <Point3D16>("ExitPoint"); Theme = element.GetAttributeValueWithDefault <byte>("Theme"); ModelTheme = element.GetAttributeValueWithDefault("ModelTheme", Theme); MusicJava = element.GetAttributeValueWithDefault <byte>("MusicJava"); Music = element.GetAttributeValueWithDefault("Music", (byte)6); Zoom = element.GetAttributeValueWithDefault("Zoom", (short)-1); var advanced = true; if (ValueIsAngle = element.AttributeCaseInsensitive("Angle") != null) { Value = element.GetAttributeValueWithDefault <short>("Angle", 22); if (element.AttributeCaseInsensitive("FieldOfView") != null) { Warning.WriteLine(string.Format(Localization.FieldOfViewIgnored, "Level")); } } else if (element.AttributeCaseInsensitive("FieldOfView") != null) { Value = element.GetAttributeValueWithDefault <short>("FieldOfView", 22); } else if (Zoom < 0) { Value = 22; } else { advanced = false; } if (Zoom >= 0 && advanced) { Warning.WriteLine(string.Format(Localization.AdvancedCameraModeDisabled, "Level", "@Angle, @FieldOfView")); } Buttons = new Buttons(this); foreach (var e in element.Elements()) { switch (e.Name.LocalName.ToLower()) { case "movingplatform": MovingPlatforms.Add(new MovingPlatform(MovingPlatforms, e)); break; case "bumper": Bumpers.Add(new Bumper(Bumpers, e)); break; case "fallingplatform": FallingPlatforms.Add(new FallingPlatform(this, e)); break; case "checkpoint": Checkpoints.Add(new Checkpoint(e)); break; case "cameratrigger": CameraTriggers.Add(new CameraTrigger(e)); break; case "prism": Prisms.Add(new Prism(e)); break; case "fan": Fans.Add(new Fan(e)); Warning.WriteLine(string.Format(Localization.DeprecatedElement, "Fan")); break; case "button": case "buttonsequence": // ReSharper disable once ObjectCreationAsStatement new Button(Buttons, e); break; case "othercube": case "darkcube": OtherCubes.Add(new OtherCube(this, e)); break; case "resizergrow": case "resizershrink": Resizers.Add(new Resizer(this, e)); break; case "miniblock": MiniBlocks.Add(new MiniBlock(e)); Warning.WriteLine(string.Format(Localization.DeprecatedElement, "MiniBlock")); break; default: Warning.WriteLine(string.Format(Localization.UnrecognizedChildElement, e.Name, "Level")); break; } } }
public static Tuple <Exception, string, List <FileEntry> > Compile(bool exFormat, string file, string directory = null) { var list = new List <FileEntry>(); var fileName = Path.GetFileNameWithoutExtension(file); if (string.IsNullOrWhiteSpace(directory)) { directory = Path.GetDirectoryName(file); } string inputPath = Path.Combine(Path.GetDirectoryName(file), fileName), outputPath = Path.Combine(directory, fileName); Warning.Start(); try { switch ((Path.GetExtension(file) ?? string.Empty).ToLowerInvariant()) { case ".bin": switch (fileName.ToLowerInvariant()) { case "cos": var array = new short[181]; using (var stream = File.OpenRead(file)) using (var reader = new BinaryReader(stream)) for (var i = 0; i <= 180; i++) { array[i] = reader.ReadInt16(); } File.WriteAllText(outputPath += ".txt", string.Join(Environment.NewLine, array.Select(value => value / 256.0))); list.Add(new FileEntry(outputPath, "cos.txt")); break; case "font": DecompileFont(file, ref outputPath); list.Add(new FileEntry(outputPath, "font.xml")); break; default: { var index = 0; foreach (var path in Level.CreateFromCompiled(file).Decompile(outputPath)) { list.Add(new FileEntry(path, index == 0 ? "level.xml" : index == 1 ? "level.png" : "level.z.png")); index++; } break; } } break; case ".xml": var root = XHelper.Load(file).Elements().First(); switch (root.Name.LocalName.ToLowerInvariant()) { case "level": { var index = 0; foreach (var path in Level.CreateFromDecompiled(inputPath) .Compile(outputPath + ".bin")) { list.Add(new FileEntry(path, index == 0 ? "level.bin" : "model.eso")); index++; } break; } case "animation": AssetHelper.ParseEan(root, fileName).Save(outputPath = Path.Combine(directory, AssetUtil.CrcFullName(fileName, "models") + ".ean")); list.Add(new FileEntry(outputPath, "animation.ean")); break; case "material": { string name; Helper.AnalyzeFileName(out name, out outputPath, fileName); var ema = AssetHelper.ParseEma(root, name); ema.Save(Path.Combine(directory, outputPath += ".ema")); list.Add(new FileEntry(outputPath, "material.ema")); break; } case "models": { string name; Helper.AnalyzeFileName(out name, out outputPath, fileName); var eso = AssetHelper.ParseEso(root, name); eso.Save(Path.Combine(directory, outputPath += ".eso")); list.Add(new FileEntry(outputPath, "model.eso")); break; } case "font": { CompileFont(inputPath, out outputPath, root); list.Add(new FileEntry(outputPath, "font.bin")); break; } } break; case ".loc": LOC.FromFile(file).SaveXls(outputPath += ".xls"); list.Add(new FileEntry(outputPath, "text.xls")); break; case ".xls": LocHelper.FromXls(file).Save(outputPath += ".loc"); list.Add(new FileEntry(outputPath, "text.loc")); break; case ".etx": var etx = ETX.FromFile(file); etx.GetBitmap().Save(outputPath = Path.Combine(directory, etx.AssetHeader.Name + ".png")); list.Add(new FileEntry(outputPath, "texture.png")); break; case ".png": { var name = AssetUtil.CrcFullName(fileName, "textures") + ".etx"; (exFormat ? (ETX)ETX1804.CreateFromImage(file) : ETX1803.CreateFromImage(file)) .Save(outputPath = Path.Combine(directory, name)); list.Add(new FileEntry(outputPath, "texture.etx")); break; } case ".ean": var ean = EAN.FromFile(file); File.WriteAllText(outputPath = Path.Combine(directory, Helper.GetDecompiledFileName(fileName, ean) + ".xml"), AssetHelper.GetEanElement(ean).ToString()); list.Add(new FileEntry(outputPath, "animation.xml")); break; case ".ema": { var ema = EMA.FromFile(file); File.WriteAllText(outputPath = Path.Combine(directory, Helper.GetDecompiledFileName(fileName, ema) + ".xml"), AssetHelper.GetEmaElement(ema).ToString()); list.Add(new FileEntry(outputPath, "material.xml")); break; } case ".eso": { var eso = ESO.FromFile(file); File.WriteAllText(outputPath = Path.Combine(directory, Helper.GetDecompiledFileName(fileName, eso) + ".xml"), AssetHelper.GetEsoElement(eso).ToString()); list.Add(new FileEntry(outputPath, "model.xml")); break; } case ".txt": using (var stream = new FileStream(outputPath += ".bin", FileMode.Create, FileAccess.Write, FileShare.Read)) { var writer = new BinaryWriter(stream); foreach (var num in File.ReadAllText(file) .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(double.Parse)) { writer.Write((short)Math.Round(num * 256)); } } list.Add(new FileEntry(outputPath, "cos.bin")); break; default: switch (Path.GetFileName(file)) { case "audio": if (Directory.Exists(outputPath = Path.Combine(directory, "sfx"))) { Directory.Delete(outputPath, true); } Directory.CreateDirectory(outputPath); string xsb = Path.Combine(file, "sfx.xsb"), xwb = Path.Combine(file, "sfx.xwb"); int offset; using (var stream = new FileStream(xsb, FileMode.Open)) { stream.Position = 0x2A; using (var reader = new BinaryReader(stream)) offset = reader.ReadInt32(); } var unxwb = new Process { StartInfo = new ProcessStartInfo( Path.Combine(CurrentApp.Directory, "Resources/Libraries/unxwb.exe"), FormattableString.Invariant($"-d \"{outputPath}\" -b \"{xsb}\" {offset} \"{xwb}\"")) { UseShellExecute = false, CreateNoWindow = true } }; unxwb.Start(); unxwb.WaitForExit(); list.Add(new FileEntry(outputPath, "sfx")); break; case "sfx": Directory.CreateDirectory(outputPath = Path.Combine(directory, "audio")); string tempPath = Helper.GetRandomDirectory(), tempInputPath = Path.Combine(tempPath, "sfx"), tempOutputPath = Path.Combine(tempPath, "audio"); /************************************************************************************ * create temp input dir because it's still occupied for unknown reason; * * create temp output dir because COMException will be thrown if output dir and * * temp input dir is not under the same drive. * ************************************************************************************/ Directory.CreateDirectory(tempInputPath); var projectPath = GenerateXactProject(file, tempInputPath); using (var project = new CXACTMasterProject()) { project.Create(); project.Load(projectPath, new CXACTMasterProjectCallback(), 0); project.Build(new CXACTMasterProjectCallback(), tempOutputPath, false, false); } foreach (var path in Directory.EnumerateFiles(tempOutputPath)) { var target = Path.Combine(outputPath, Path.GetFileName(path)); File.Delete(target); File.Move(path, target); } try { Directory.Delete(tempPath, true); } catch { Trace.WriteLine(tempPath, "Delete tempPath failed"); } list.Add(new FileEntry(outputPath, "audio")); break; default: throw new NotSupportedException(Localization.DecompileUnrecognizedFile); } break; } return(new Tuple <Exception, string, List <FileEntry> >(null, Warning.Fetch(), list)); } catch (Exception exc) { return(new Tuple <Exception, string, List <FileEntry> >(exc, Warning.Fetch(), list)); } finally { Warning.Clear(); } }