internal void SaveLibrary(string filepath, List <J2TFile> Tilesets, int numberOfExtraDataLevels, WeaponsForm.ExtendedWeapon[] customWeapons) { var encoding = J2LFile.FileEncoding; List <string> RequiredFilenames = new List <string>(); bool weaponLibrary = false; foreach (var cw in customWeapons) { if (cw != null) { RequiredFilenames.Add(cw.LibraryFilename); weaponLibrary = true; } } string AngelscriptLibraryFilename = "MLLE-Include-" + CurrentMLLEData5VersionString + (!weaponLibrary ? "" : "w") + ".asc"; using (BinaryWriter binwriter = new BinaryWriter(File.Open(Path.Combine(Path.GetDirectoryName(filepath), AngelscriptLibraryFilename), FileMode.Create, FileAccess.Write), encoding)) { binwriter.Write(encoding.GetBytes("//")); var libraryFileAsBytes = encoding.GetBytes(string.Format( AngelscriptLibrary, AngelscriptLibraryFilename, !weaponLibrary ? "" : AngelscriptLibraryWeaponsPortion1, !weaponLibrary ? "" : AngelscriptLibraryWeaponsPortion2, !weaponLibrary ? "" : AngelscriptLibraryWeaponsPortion3, !weaponLibrary ? "" : AngelscriptLibraryWeaponsPortion4 )); CRC32 CRCCalculator = new CRC32(); CRCCalculator.SlurpBlock(libraryFileAsBytes, 0, libraryFileAsBytes.Length); binwriter.Write(encoding.GetBytes(((uint)CRCCalculator.Crc32Result).ToString() + "\r\n")); binwriter.Write(libraryFileAsBytes); } string scriptFilepath = Path.ChangeExtension(filepath, ".j2as"); string fileContents = ""; if (File.Exists(scriptFilepath)) { fileContents = System.IO.File.ReadAllText(scriptFilepath, encoding); } RequiredFilenames.Add(Path.GetFileName(filepath)); for (int i = 1; i < Tilesets.Count; ++i) { RequiredFilenames.Add(Tilesets[i].FilenameOnly); } int extraDataLevelID = 0; for (extraDataLevelID = 0; extraDataLevelID < numberOfExtraDataLevels; ++extraDataLevelID) { RequiredFilenames.Add(Path.GetFileName(GetExtraDataLevelFilepath(filepath, extraDataLevelID))); } foreach (string fn in RequiredFilenames) { string pragma = GetPragmaRequire(fn); if (!fileContents.Contains(pragma)) { fileContents = pragma + TagForProgrammaticallyAddedLines + fileContents; } if (fn.EndsWith("asc", System.StringComparison.InvariantCultureIgnoreCase)) { pragma = GetPragmaInclude(fn); if (!fileContents.Contains(pragma)) { fileContents = pragma + TagForProgrammaticallyAddedLines + fileContents; } } } while (true) //remove extra such pragmas/files if the number of layers has decreased since the last time this level was saved { string extraFilepath = GetExtraDataLevelFilepath(filepath, extraDataLevelID++); File.Delete(extraFilepath); string pragma = GetPragmaRequire(Path.GetFileName(extraFilepath)); if (fileContents.Contains(pragma)) { fileContents = new Regex("^[^\\n]*" + pragma + "\\s*?\\r?\\n?", RegexOptions.Multiline).Replace(fileContents, ""); } else { break; } } fileContents = "#include \"" + AngelscriptLibraryFilename + "\"" + TagForProgrammaticallyAddedLines + fileContents; Regex setupPattern = new Regex(@"MLLE\s*::\s*Setup\s*\([^;]*\)\s*;"); string desiredSetupCall = "MLLE::Setup("; if (weaponLibrary) { desiredSetupCall += "array<MLLEWeaponApply@> = {"; for (int i = 0; i < 9; ++i) { WeaponsForm.ExtendedWeapon cw = customWeapons[i]; if (cw != null) { desiredSetupCall += cw.Initialization; } else { desiredSetupCall += "null"; } if (i < 8) { desiredSetupCall += ", "; } } desiredSetupCall += "}"; } desiredSetupCall += ");"; Match match = setupPattern.Match(fileContents); if (!match.Success) { fileContents = "const bool MLLESetupSuccessful = " + desiredSetupCall + TagForProgrammaticallyAddedLines + fileContents; } else if (match.Value != desiredSetupCall) { fileContents = setupPattern.Replace(fileContents, desiredSetupCall); } bool[] hooksNeeded = WeaponHookSpecs.Select(ss => weaponLibrary && customWeapons.Any(cw => cw != null && new Regex("\\b" + ss[1] + "\\b", RegexOptions.IgnoreCase).Match(cw.Hooks).Success)).ToArray(); hooksNeeded[3] = weaponLibrary; //onDrawAmmo is used by ALL custom weapons for (int specID = 0; specID < WeaponHookSpecs.Length; ++specID) { string[] spec = WeaponHookSpecs[specID]; Regex weaponHookCallFindRegex = new Regex(@"(return\s+)?MLLE\s*::\s*WeaponHook\s*\.\s*" + spec[2] + @"\s*\([^;]*\)(\s*;)?"); string functionPattern = "(" + spec[0] + @"\s+" + spec[1] + @"\s*\(\s*"; for (int paramStringID = 3; paramStringID < spec.Length; paramStringID += 2) { functionPattern += spec[paramStringID].Replace(" ", @"\s*") + @"\s*(\S+)\s*"; if (paramStringID + 2 < spec.Length) { functionPattern += @",\s*"; } } functionPattern += @"\)[^{]*{)"; //e.g. a pattern to match "void onMain() {" if (hooksNeeded[specID]) { if (!weaponHookCallFindRegex.Match(fileContents).Success) //weaponhook call not being made { string weaponhookMethodCall = "\r\n\t" + (spec[0] == "void" ? "" : "return ") + "MLLE::WeaponHook." + spec[2] + "("; for (int paramStringID = 3; paramStringID < spec.Length; paramStringID += 2) { weaponhookMethodCall += "$" + ((paramStringID + 1) / 2).ToString(); if (paramStringID + 2 < spec.Length) { weaponhookMethodCall += ", "; } } weaponhookMethodCall += ");"; //e.g. "WeaponHook::processMain();" Regex functionRegex = new Regex(functionPattern); match = functionRegex.Match(fileContents); if (match.Success) //hook function already exists { fileContents = functionRegex.Replace(fileContents, "$1" + weaponhookMethodCall); } else { //add everything from scratch string functionToAdd = "\r\n" + spec[0] + " " + spec[1] + "("; for (int paramStringID = 3; paramStringID < spec.Length; paramStringID += 2) { functionToAdd += spec[paramStringID].Replace(" ", "") + " " + spec[paramStringID + 1]; weaponhookMethodCall = weaponhookMethodCall.Replace("$" + ((paramStringID + 1) / 2).ToString(), spec[paramStringID + 1]); if (paramStringID + 2 < spec.Length) { functionToAdd += ", "; } } functionToAdd += ") {" + weaponhookMethodCall + "\r\n}\r\n"; fileContents += functionToAdd; } } } else //hook NOT needed { fileContents = new Regex(functionPattern + @"[\s;]*}\r?\n?").Replace(weaponHookCallFindRegex.Replace(fileContents, ""), ""); //remove the method call, then remove the hook function it was in if that hook is now totally empty } } System.IO.File.WriteAllText(scriptFilepath, fileContents, encoding); }
internal bool CreateData5Section(out byte[] Data5, out WeaponsForm.ExtendedWeapon[] CustomWeapons, List <J2TFile> Tilesets, List <Layer> Layers) { Data5 = null; CustomWeapons = new WeaponsForm.ExtendedWeapon[9]; var data5header = new MemoryStream(); using (MemoryStream data5body = new MemoryStream()) using (BinaryWriter data5writer = new BinaryWriter(data5header, J2LFile.FileEncoding)) using (BinaryWriter data5bodywriter = new BinaryWriter(data5body, J2LFile.FileEncoding)) { data5writer.Write(MLLEData5MagicString.ToCharArray()); data5writer.Write(CurrentMLLEData5Version); data5bodywriter.Write(IsSnowing); data5bodywriter.Write(IsSnowingOutdoorsOnly); data5bodywriter.Write(SnowingIntensity); data5bodywriter.Write((byte)SnowingType); data5bodywriter.Write(WarpsTransmuteCoins); data5bodywriter.Write(DelayGeneratedCrateOrigins); data5bodywriter.Write(Echo); data5bodywriter.Write(DarknessColor.ToArgb()); data5bodywriter.Write(WaterChangeSpeed); data5bodywriter.Write((byte)WaterInteraction); data5bodywriter.Write(WaterLayer); data5bodywriter.Write((byte)WaterLighting); data5bodywriter.Write(WaterLevel); data5bodywriter.Write(WaterGradientStart.ToArgb()); data5bodywriter.Write(WaterGradientStop.ToArgb()); data5bodywriter.Write(Palette != null); if (Palette != null) { Palette.WriteLEVStyle(data5bodywriter); } foreach (byte[] remappings in ColorRemappings) { if (remappings == null) { data5bodywriter.Write(false); } else { data5bodywriter.Write(true); for (int i = 0; i < remappings.Length; ++i) { data5bodywriter.Write(remappings[i]); } } } data5bodywriter.Write((byte)(Tilesets.Count - 1)); for (int tilesetID = 1; tilesetID < Tilesets.Count; ++tilesetID) //Tilesets[0] is already mentioned in Data1, after all { var tileset = Tilesets[tilesetID]; data5bodywriter.Write(Path.ChangeExtension(tileset.FilenameOnly, ".j2t")); //convert "J2T" to "j2t" so JJ2+ doesn't reject the jjTilesFromTileset call data5bodywriter.Write((ushort)tileset.FirstTile); data5bodywriter.Write((ushort)tileset.TileCount); byte[] remappings = tileset.ColorRemapping; data5bodywriter.Write(remappings != null); if (remappings != null) { for (int i = 0; i < remappings.Length; ++i) { data5bodywriter.Write(remappings[i]); } } } data5bodywriter.Write((uint)Layers.Count); foreach (Layer layer in Layers) { data5bodywriter.Write((sbyte)layer.id); data5bodywriter.Write(layer.Name); data5bodywriter.Write(layer.Hidden); data5bodywriter.Write(layer.SpriteMode); data5bodywriter.Write(layer.SpriteParam); data5bodywriter.Write(layer.RotationAngle); data5bodywriter.Write(layer.RotationRadiusMultiplier); } int levelTileCount = Tilesets.Sum(ts => (int)ts.TileCount); foreach (byte[][] images in new byte[][][] { TileImages, TileMasks }) { int numberOfImages = images.Take(levelTileCount).Count(it => it != null); data5bodywriter.Write((ushort)numberOfImages); if (numberOfImages > 0) { for (int i = 1; i < levelTileCount; ++i) { byte[] image = images[i]; if (image != null) { data5bodywriter.Write((ushort)i); data5bodywriter.Write(image); } } } } for (int weaponID = 0; weaponID < Weapons.Length; ++weaponID) { Weapon weapon = Weapons[weaponID]; Weapon defaultWeapon = WeaponDefaults[weaponID]; bool isGun8InSlot8 = weaponID == 7 && weapon.Name == "Gun8"; //hardcoded laxer standards... parameter need not match bool isCustom = !isGun8InSlot8 && !(weapon.Name.Equals(defaultWeapon.Name) && weapon.Options.Skip(Weapon.NumberOfCommonOptions).SequenceEqual(defaultWeapon.Options.Skip(Weapon.NumberOfCommonOptions))); data5bodywriter.Write(isCustom); int[] options = weapon.Options; data5bodywriter.Write(options[0]); data5bodywriter.Write((byte)options[1]); data5bodywriter.Write(options[2] != 0); data5bodywriter.Write((byte)options[3]); data5bodywriter.Write((byte)options[4]); data5bodywriter.Write((byte)options[5]); if (weaponID == 6) { data5bodywriter.Write(Gun7Crate); } else if (weaponID == 7) { data5bodywriter.Write(Gun8Crate); } else if (weaponID == 8) { data5bodywriter.Write(Gun9Crate); } if (isGun8InSlot8) { data5bodywriter.Write((byte)options[Weapon.NumberOfCommonOptions]); } else if (isCustom) { data5bodywriter.Write(weapon.Name); var extendedWeapon = WeaponsForm.GetAllAvailableWeapons().Find(w => w.Name == weapon.Name); if (extendedWeapon == null) { MessageBox.Show(String.Format("Sorry, the MLLE \"Weapons\" folder did not include any .ini file defining a weapon with the name \"{0}.\"", weapon.Name), "Weapon not found", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } CustomWeapons[weaponID] = extendedWeapon; data5bodywriter.Write(extendedWeapon.OptionTypes.Skip(Weapon.NumberOfCommonOptions).Select(o => o == WeaponsForm.ExtendedWeapon.oTypes.Int ? sizeof(int) : sizeof(byte)).Sum()); for (int optionID = Weapon.NumberOfCommonOptions; optionID < options.Length; ++optionID) { switch (extendedWeapon.OptionTypes[optionID]) { case WeaponsForm.ExtendedWeapon.oTypes.Bool: data5bodywriter.Write(options[optionID] != 0); break; case WeaponsForm.ExtendedWeapon.oTypes.Dropdown: data5bodywriter.Write((byte)options[optionID]); break; case WeaponsForm.ExtendedWeapon.oTypes.Int: default: data5bodywriter.Write(options[optionID]); break; } } } } var data5bodycompressed = ZlibStream.CompressBuffer(data5body.ToArray()); data5writer.Write((uint)data5bodycompressed.Length); data5writer.Write((uint)data5body.Length); data5writer.Write(data5bodycompressed); } Data5 = data5header.ToArray(); return(true); }