jmerry Posted September 23, 2023 Author Share Posted September 23, 2023 No sanity check. But a size-based check wouldn't catch this one anyway; a standard EFF is 272 bytes, more than twice the size of a minimal ITM or SPL. To catch this one, I'd have to read the first few bytes and compare them to the extension. Quote Link to comment
Connelly Posted October 18, 2023 Share Posted October 18, 2023 In another bit of weirdness, the "Death Ward against Aec'Letec" component just tripped on Detect Alignment: ERROR: illegal 2-byte read from offset 2402 of 2402-byte file SPWI202.SPL ERROR: [SPWI202.SPL] -> [override/SPWI202.SPL] Patching Failed (COPY) (Failure("SPWI202.SPL: read out of bounds")) I suspect aTweaks' "Make alignment detection spells more accurate" might he the source of the problem, that's on it. But uh I gotta ask why would a mod for Death Ward have anything to do with Detect Alignment? Wouldn't that be in the complete opposite side of what the mod should look up? Quote Link to comment
jmerry Posted October 18, 2023 Author Share Posted October 18, 2023 It wouldn't have anything to do with Detect Alignment, but it's a global scan through all spells and items so it can trip on anything malformed. The only thing doing any reads is the built-in CLONE_EFFECT function, which obscures everything behind a black box of how that works. Right, Detect Alignment ... actually, SPWI202 is Detect Evil in the base game. And I have no idea what aTweaks does to it, because that appears to be an SHS-hosted mod and I don't see it on the SHS github. Thinking on what might be happening here... first, 2402 bytes? Vanilla Detect Evil is only 298. Whatever aTweaks is doing here greatly expands the file. Anywhay, CLONE_EFFECT has to scan through stuff, and in my use it's looking for matches in opcode (2 bytes, beginning of effect substructure) and parameter 2 (4 bytes, some way in). My guess here is a mis-indexed file; it says there's at least one more effect than there actually is, so the numbering points to an effect that starts at the end of the file, the function tries to read that, and splat. Wait, I found links. Old version: https://github.com/FredrikLindgren/aTweaks Newer fork: https://github.com/TotoR115/aTweaks Here's the code for what the component does (old version): Spoiler // Detect Evil ACTION_FOR_EACH ~file~ IN // for each of the following files ~SPPR104~ // Detect Evil (Divine version) ~SPWI202~ // Detect Evil (Arcane version) ~SPCL212~ // Detect Evil (Paladin innate) ~SPIN696~ // Moon Dog Sight (Moon Dog figurine) ~CDDETEVL~ // G2 BG2 Fixpack Detect Evil (shell spell) BEGIN // execute the following ACTION_IF FILE_EXISTS_IN_GAME ~%file%.spl~ BEGIN // if the designated file with a SPL extension exists COPY_EXISTING ~%file%.spl~ ~override~ PATCH_IF (%SOURCE_SIZE% > 0x71) THEN BEGIN // file size check // =============================================================================== // the actual work starts from here READ_LONG 0x64 "abil_off" ELSE 0 READ_SHORT 0x68 "abil_num" ELSE 0 READ_LONG 0x6a "fx_off" ELSE 0 SET "delta" = 0 FOR (index = 0 ; index < abil_num ; index = index + 1) BEGIN READ_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) "abil_fx_num" READ_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "abil_fx_idx" SET "abil_fx_idx" = ("%abil_fx_idx%" + "%delta%") WRITE_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "%abil_fx_idx%" FOR (index2 = 0 ; index2 < abil_fx_num ; index2 = index2 + 1) BEGIN READ_SHORT ("%fx_off%" + (0x30 * ("%abil_fx_idx%" + "%index2%"))) "opcode" PATCH_IF ("%opcode%" = 115) BEGIN // clone effect #115: Detect Alignment READ_ASCII ("%fx_off%" + (("%abil_fx_idx%" + "%index2%") * 0x30)) "clone_fx" (0x30) SET "index2"= "%abil_fx_num%" // kills loop INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect WRITE_EVALUATED_ASCII ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "%clone_fx%" // cloned effect WRITE_SHORT ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "177" // effect #177: use EFF File WRITE_LONG ("%fx_off%" + 0x04 + ("%abil_fx_idx%" * 0x30)) "3" // param1: 3 (IDS Entry - MASK_EVIL) WRITE_LONG ("%fx_off%" + 0x08 + ("%abil_fx_idx%" * 0x30)) "8" // param2: 8 (IDS File - ALIGN.IDS) WRITE_BYTE ("%fx_off%" + 0x0c + ("%abil_fx_idx%" * 0x30)) "0" // timing: 0 (Duration) WRITE_LONG ("%fx_off%" + 0x0e + ("%abil_fx_idx%" * 0x30)) "1" // duration: 1 WRITE_ASCII ("%fx_off%" + 0x14 + ("%abil_fx_idx%" * 0x30)) ~RR#DAGLR~ #8 // resref: RR#DAGLR.EFF (evil alignments glow red) INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect WRITE_EVALUATED_ASCII ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "%clone_fx%" // cloned effect WRITE_SHORT ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "177" // effect #177: use EFF File WRITE_LONG ("%fx_off%" + 0x04 + ("%abil_fx_idx%" * 0x30)) "19" // param1: 19 (IDS Entry - LAWFUL_EVIL) WRITE_LONG ("%fx_off%" + 0x08 + ("%abil_fx_idx%" * 0x30)) "8" // param2: 8 (IDS File - ALIGN.IDS) WRITE_BYTE ("%fx_off%" + 0x0c + ("%abil_fx_idx%" * 0x30)) "1" // timing: 1 (Permanent) WRITE_LONG ("%fx_off%" + 0x0e + ("%abil_fx_idx%" * 0x30)) "0" // duration: 0 WRITE_ASCII ("%fx_off%" + 0x14 + ("%abil_fx_idx%" * 0x30)) ~RR#DASLE~ #8 // resref: RR#DASLE.EFF (display string - Lawful Evil) INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect WRITE_EVALUATED_ASCII ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "%clone_fx%" // cloned effect WRITE_SHORT ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "177" // effect #177: use EFF File WRITE_LONG ("%fx_off%" + 0x04 + ("%abil_fx_idx%" * 0x30)) "35" // param1: 35 (IDS Entry - NEUTRAL_EVIL) WRITE_LONG ("%fx_off%" + 0x08 + ("%abil_fx_idx%" * 0x30)) "8" // param2: 8 (IDS File - ALIGN.IDS) WRITE_BYTE ("%fx_off%" + 0x0c + ("%abil_fx_idx%" * 0x30)) "1" // timing: 1 (Permanent) WRITE_LONG ("%fx_off%" + 0x0e + ("%abil_fx_idx%" * 0x30)) "0" // duration: 0 WRITE_ASCII ("%fx_off%" + 0x14 + ("%abil_fx_idx%" * 0x30)) ~RR#DASNE~ #8 // resref: RR#DASNE.EFF (display string - Neutral Evil) INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect WRITE_EVALUATED_ASCII ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "%clone_fx%" // cloned effect WRITE_SHORT ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) "177" // effect #177: use EFF File WRITE_LONG ("%fx_off%" + 0x04 + ("%abil_fx_idx%" * 0x30)) "51" // param1: 51 (IDS Entry - CHAOTIC_EVIL) WRITE_LONG ("%fx_off%" + 0x08 + ("%abil_fx_idx%" * 0x30)) "8" // param2: 8 (IDS File - ALIGN.IDS) WRITE_BYTE ("%fx_off%" + 0x0c + ("%abil_fx_idx%" * 0x30)) "1" // timing: 1 (Permanent) WRITE_LONG ("%fx_off%" + 0x0e + ("%abil_fx_idx%" * 0x30)) "0" // duration: 0 WRITE_ASCII ("%fx_off%" + 0x14 + ("%abil_fx_idx%" * 0x30)) ~RR#DASCE~ #8 // resref: RR#DASCE.EFF (display string - Chaotic Evil) SET "delta" = "%delta%" + 4 WRITE_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) ("%abil_fx_num%" + 4) END END END SET opcode_to_delete = "115" // mark effect #115 (Detect Alignment) for deletion SET header = "-1" // mark all extended headers for deletion LAUNCH_PATCH_MACRO ~DELETE_SPELL_EFFECT~ // delete the designated opcodes from all of the spell's extended headers // =============================================================================== // the actual work ends here END // ends file size check BUT_ONLY_IF_IT_CHANGES END // ends ACTION_IF FILE_EXISTS_IN_GAME block END // ends ACTION_FOR_EACH block It goes low-level all the way. So if it makes a mistake, no guards against that going wrong. And checking the other version ... no obvious differences in this part. So what does that code do? Basically, it's a hand-done CLONE_EFFECT. And I don't see anything wrong with it. But the component has more code ... wait. Do you have Spell Revisions installed as well? Because there's a "compatibility block" for SR, and I think that one can break the indexing. The code above (for all versions of the spell) inserts four new effects for an instance of opcode 115, adds 4 to the number of effects in this header, increments the effect indexing for later headers by 4, and then runs DELETE_EFFECT on opcode 115 which would catch any mis-indexing. All good. The SR "compatibility block" inserts nine new effects for an instance of opcode 177, adds 9 to the number of effects in this header, increments the effect indexing for later headers by 12, and then doesn't run any sort of macro that would catch mis-indexing. And it's its own loop, so ... yeah. But wait - even this won't actually break, because SPWI202 only has one header, even in the SR version. Unless some other mod came around in the interim and expanded the spell with additional headers. Which, given the size - a one-header spell with 16 effects is only about 900 bytes - is looking distinctly likely. All right, just how many mods do you have that mess with this spell? Quote Link to comment
subtledoctor Posted October 18, 2023 Share Posted October 18, 2023 Several mods add CF effects? SCS adds a few, I know. My psionics mod adds one. Those could make the file bigger. Not by very much, though. Isn’t each effect only 40 bytes? EDIT - and Tome & Blood’s Revised Specialists adds a single new header, with min_level = 51, with a bunch of effects. Quote Link to comment
jmerry Posted October 18, 2023 Author Share Posted October 18, 2023 So, probably the new header. Looks like SR -> T&B -> aTweaks can break this spell, because of the shoddy re-indexing in aTweaks. And it still might cast fine, unless you did whatever it is that uses the header - using that memorization slot for a spontaneous spell in your specialty? But anything that tries to parse the spell fully is liable to error out with a bad read. Quote Link to comment
Connelly Posted October 18, 2023 Share Posted October 18, 2023 I do have SR as well as T&B. That said, @subtledoctor the Spell Tweaks components "Revised Invisibility and True Seeing" and "Change SR Expeditious Retreat into Chameleon" also seem to touch this spell. The last one in fact seems to increase the file size from 298 to 346 bytes, before T&B's Revised Specialists lifts it to 1.45 kb (the header I guess?). Jmerry is convincing me that atweaks is the bigger troublemaker, but could be worth a look into those two? Quote Link to comment
subtledoctor Posted October 18, 2023 Share Posted October 18, 2023 (edited) Yeah Expeditious Retreat-> Chameleon adds an op321 effect canceling chameleon. Revised Invisibility… I don’t remember, it might do something similar. But those just use a basic LPF ADD_SPELL_EFFECT function, it is fairly bulletproof AFAIK and should not corrupt the file structure. He suspects aTweaks because it is doing low-level patching, like inserting bytes directly into the file, which can throw everything off if not done right. EDIT - @jmerry is there anything I can do in TnB to add that new header more cleanly? Or will aTweaks choke on it regardless it it has an extra header. Edited October 19, 2023 by subtledoctor Quote Link to comment
jmerry Posted October 18, 2023 Author Share Posted October 18, 2023 The mere presence of any extra header with at least one effect will do it. As long as that header comes after the one with the normal spell effects, anyway. The aTweaks component will proceed to mis-index that header so that it points to effects that aren't in the file. So as long as you're implementing your system with a "level 51" header, there's not really anything you can do about it on your end. (What happens if you put spell headers in the "wrong" order? Probably not good things; my guess is that the game ends up using the last header that the condition is met for, so a header that comes before one with a lower level minimum will never be used.) For aTweaks, it's a one-line fix; replace a "12" with a "9". Or recode that whole component to use the built-in functions instead of reinventing them. Quote Link to comment
Graion Dilach Posted October 19, 2023 Share Posted October 19, 2023 To be fair, it's likely that the component predates the built-in functions and that's why ware here now. Quote Link to comment
jmerry Posted October 19, 2023 Author Share Posted October 19, 2023 Yes, it is quite old stuff. Anyway, here's the specific code block that has the problem. Somewhat abridged, with extra comments by me. Spoiler // Special Spell Revisions compatibility block. Somewhat abridged, with additional comments by jmerry ACTION_IF MOD_IS_INSTALLED SPELL_REV.TP2 0 THEN BEGIN // SR's Arcane version of Detect Evil needs special treatment COPY_EXISTING ~SPWI202.SPL~ ~override~ // Detect Evil (Arcane version) READ_LONG 0x64 "abil_off" ELSE 0 READ_SHORT 0x68 "abil_num" ELSE 0 READ_LONG 0x6a "fx_off" ELSE 0 SET "delta" = 0 FOR (index = 0 ; index < abil_num ; index = index + 1) BEGIN READ_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) "abil_fx_num" READ_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "abil_fx_idx" // This field is the index of the header's first effect. SET "abil_fx_idx" = ("%abil_fx_idx%" + "%delta%") // So we increment it by delta. Zero for the first header. WRITE_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "%abil_fx_idx%" // And the number of effects we added for later headers. FOR (index2 = 0 ; index2 < abil_fx_num ; index2 = index2 + 1) BEGIN READ_SHORT ("%fx_off%" + (0x30 * ("%abil_fx_idx%" + "%index2%"))) "opcode" PATCH_IF ("%opcode%" = 177) BEGIN // clone effect #177: Use EFF file READ_ASCII ("%fx_off%" + (("%abil_fx_idx%" + "%index2%") * 0x30)) "clone_fx" (0x30) SET "index2"= "%abil_fx_num%" // kills loop INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #1 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #2 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #3 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #4 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #5 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #6 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #7 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #8 // Abridged; write data for inserted effect here INSERT_BYTES ("%fx_off%" + ("%abil_fx_idx%" * 0x30)) 0x30 // new effect #9 // Abridged; write data for inserted effect here SET "delta" = "%delta%" + 12 // 9 more effects in this header, so increase effect indexes in later headers by 12? Broken. WRITE_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) ("%abil_fx_num%" + 9) // 9 more effects in this header, so increment that field. END END END COPY ~atweaks/spl/elementals/rr#ekal2.spl~ ~override/rr#ekal.spl~ // Know Alignment (aTweaks Elemental Prince version) END // ends SR compatibility block This code block is present in both versions of the project that I linked to. The fork, at least, looks to still be actively maintained. Quote Link to comment
ktchong Posted February 4 Share Posted February 4 Received a warning when re-installing the Shapeshift Correction component of jTweaks v3.1. Otherwise, everything else was installed smoothly, (mostly.) [setup-jtweaks.exe] WeiDU version 24900 {setup-atweaks.exe} Queried (pid = 408) version = 24900 {setup-dragonspear_ui++.exe} Queried (pid = 436) version = 24900 Using Language [English] Using .\lang\en_us\dialog.tlk . . . Installing [Shapeshift corrections -> All transformations] [3.1] Copying and patching 1 file ... WARNING: no effects altered on SPCL612.SPL Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 2 files ... Copying and patching 2 files ... Copying and patching 2 files ... Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 1 file ... Copying and patching 13 files ... [.\lang\en_us\dialog.tlk] created, 91583 string entries INSTALLED WITH WARNINGS Shapeshift corrections -> All transformations . . . WeiDU.log is attached. jTweaks are the last components in the log. WeiDU.log Quote Link to comment
jmerry Posted February 4 Author Share Posted February 4 The warning is harmless. However, you should uninstall that component; it's meant to fix minor issues with the vanilla shapeshifts, and you have a much more thorough overhaul already installed (SCS #2040 for wizard shapeshifts, SCS #4030 for druid shapeshifts) which fundamentally changes what those spells and abilities do. With my component installed over that, you'll have inaccurate descriptions and likely worse. ... You know, I think I'll add a "forbid" condition and enforce the incompatibility for the next version. Quote Link to comment
guyudennis Posted February 4 Share Posted February 4 Or recommend (in the documentation) that the bug fixes component in its entirety should best be installed as early as possible? That’s how I install it always. Quote Link to comment
ktchong Posted March 18 Share Posted March 18 (edited) On 1/21/2022 at 5:12 PM, jmerry said: 48. Variable drow magic resistance - Choose base drow MR (required). Three options: Low (35), Medium (50), High (65). - Choose MR progression rate (required). Three options: none, slow (1/level to 30), fast (2/level to 20) - Choose sunlight penalties (required). Two options: none, Dazzled (-2 THAC0, -2 missile AC) The components are broken. I installed the medium option (50), fast progression (2/level to 20), and dazzled. It set Viconia's base MR to 0. MR did NOT improve when she leveled up several times. It stayed at 0. She was dazzled ALL the time, even during nighttime. I tried to use EE Keeper to edit her character and give her 50 MR... which did NOT work. When I loaded the game, her MR was reset to 0. I tested with another character (main character) and edited his MR to 50. Loaded the game, and the 50 MR was there. Viconia was in the party in the same game, (I had also edited her MR to 50,) and her MR showed up as 0. She was dazzled while indoor at nighttime. I saved the game, and Viconia's MR was reset to 0. It seemed like the dazzle somehow overrode and interfered with her MR. Baeloth's MR was 67 at level 6 with Robe of Evil Archmagi (MR +5). He was NOT dazzled at all, not even during daytime. That is why I suspected the problem was dazzled: he was not affected by dazzled, which was why his MR was not overridden. BTW, the +2/level calculation was off. Baeloth's base MR should be: 50 at level 1, 52 at level 2, 54 at level 3, 56 at level 4, 58 at level 5, and 60 at level 6. The Archmagi robe gave him another 5 MR. So his total MR at level 6, with the Archmagi Robe, should be 60 + 5 = 65. His base MR should start at 50 at level 1. The first +2 progression should start at level 2, not level 1. Edited March 18 by ktchong Quote Link to comment
ktchong Posted March 18 Share Posted March 18 (edited) Just uninstalled the components, (i.e., all three sub-components,) and rolled back to just before saving/recruiting Viconia. Her MR was 50 when recruited, and stayed at 50 after saving/reloading. So the "dazzled" component DID break Viconia and her MR. Rolled back to before the components: Baeloth's MR is 55, (50 base + 5 from Archmagi Robe.) Edited March 18 by ktchong Quote Link to comment
Recommended Posts
Join the conversation
You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.