From b8813ee6c74830ccd7a31ddcc1cbe00d319ae059 Mon Sep 17 00:00:00 2001 From: VladislavKolosovsky Date: Tue, 30 Jun 2015 00:07:12 +0700 Subject: [PATCH] commit latest source from official SourceForge repo --- .gitignore | 194 ++ MPClient/DebugEditor.Designer.cs | 182 ++ MPClient/DebugEditor.cs | 300 +++ MPClient/DebugEditor.resx | 132 ++ MPClient/EditorConnection.cs | 103 + MPClient/EditorWindow.Designer.cs | 116 ++ MPClient/EditorWindow.cs | 56 + MPClient/EditorWindow.resx | 138 ++ MPClient/MPClient.csproj | 76 + MPClient/MPClient.sln | 19 + MPClient/Program.cs | 28 + MPClient/Properties/AssemblyInfo.cs | 33 + sfall/.editorconfig | 15 + sfall/AI.cpp | 126 ++ sfall/AI.h | 26 + sfall/AmmoMod.cpp | 784 ++++++++ sfall/AmmoMod.h | 21 + sfall/AnimationsAtOnceLimit.cpp | 378 ++++ sfall/AnimationsAtOnceLimit.h | 26 + sfall/Arrays.cpp | 605 ++++++ sfall/Arrays.h | 173 ++ sfall/BarBoxes.cpp | 99 + sfall/BarBoxes.h | 4 + sfall/Books.cpp | 116 ++ sfall/Books.h | 21 + sfall/BurstMods.cpp | 82 + sfall/BurstMods.h | 21 + sfall/CRC.cpp | 83 + sfall/CRC.h | 19 + sfall/Console.cpp | 83 + sfall/Console.h | 23 + sfall/Credits.cpp | 127 ++ sfall/Credits.h | 21 + sfall/Criticals.cpp | 253 +++ sfall/Criticals.h | 26 + sfall/DebugEditor.cpp | 238 +++ sfall/DebugEditor.h | 3 + sfall/Define.h | 253 +++ sfall/Elevators.cpp | 162 ++ sfall/Elevators.h | 21 + sfall/Explosions.cpp | 218 +++ sfall/Explosions.h | 23 + sfall/FalloutEngine.cpp | 162 ++ sfall/FalloutEngine.h | 120 ++ sfall/FalloutStructs.h | 157 ++ sfall/FileSystem.cpp | 708 +++++++ sfall/FileSystem.h | 42 + sfall/Graphics.h | 42 + sfall/HeroAppearance.cpp | 2814 +++++++++++++++++++++++++++ sfall/HeroAppearance.h | 62 + sfall/HookScripts.cpp | 1224 ++++++++++++ sfall/HookScripts.h | 36 + sfall/Imports.cpp | 228 +++ sfall/Imports.h | 48 + sfall/Inventory.cpp | 618 ++++++ sfall/Inventory.h | 24 + sfall/KillCounter.cpp | 115 ++ sfall/KillCounter.h | 22 + sfall/Knockback.cpp | 341 ++++ sfall/Knockback.h | 29 + sfall/LoadGameHook.cpp | 567 ++++++ sfall/LoadGameHook.h | 43 + sfall/Logging.cpp | 68 + sfall/Logging.h | 38 + sfall/MainMenu.cpp | 109 ++ sfall/MainMenu.h | 19 + sfall/Numbers.cpp | 53 + sfall/Numbers.h | 4 + sfall/PartyControl.cpp | 249 +++ sfall/PartyControl.h | 22 + sfall/Perks.cpp | 1100 +++++++++++ sfall/Perks.h | 48 + sfall/Premade.cpp | 62 + sfall/Premade.h | 19 + sfall/QuestList.cpp | 375 ++++ sfall/QuestList.h | 19 + sfall/Reputations.cpp | 56 + sfall/Reputations.h | 1 + sfall/SafeWrite.cpp | 62 + sfall/SafeWrite.h | 17 + sfall/ScriptExtender.cpp | 1568 +++++++++++++++ sfall/ScriptExtender.h | 106 + sfall/ScriptOps/AnimOps.hpp | 200 ++ sfall/ScriptOps/AsmMacros.h | 184 ++ sfall/ScriptOps/FileSystemOps.hpp | 531 +++++ sfall/ScriptOps/GraphicsOp.hpp | 555 ++++++ sfall/ScriptOps/InterfaceOp.hpp | 460 +++++ sfall/ScriptOps/MemoryOp.hpp | 337 ++++ sfall/ScriptOps/MiscOps.hpp | 1743 +++++++++++++++++ sfall/ScriptOps/ObjectsOps.hpp | 564 ++++++ sfall/ScriptOps/PerksOp.hpp | 668 +++++++ sfall/ScriptOps/ScriptArrays.hpp | 666 +++++++ sfall/ScriptOps/ScriptUtils.hpp | 877 +++++++++ sfall/ScriptOps/StatsOp.hpp | 1323 +++++++++++++ sfall/ScriptOps/WorldmapOps.hpp | 221 +++ sfall/ScriptOps/readme.txt | 1 + sfall/Stats.cpp | 305 +++ sfall/Stats.h | 28 + sfall/SuperSave.cpp | 595 ++++++ sfall/SuperSave.h | 27 + sfall/Tiles.cpp | 317 +++ sfall/Tiles.h | 21 + sfall/ddraw.cpp | 32 + sfall/ddraw.sln | 29 + sfall/ddraw.vcxproj | 450 +++++ sfall/ddraw.vcxproj.filters | 335 ++++ sfall/ddraw2.cpp | 911 +++++++++ sfall/dinput.cpp | 334 ++++ sfall/exports.def | 6 + sfall/heads.cpp | 190 ++ sfall/heads.h | 1 + sfall/input.h | 172 ++ sfall/main.cpp | 1786 +++++++++++++++++ sfall/main.h | 56 + sfall/movies.cpp | 559 ++++++ sfall/movies.h | 28 + sfall/queue9x.cpp | 59 + sfall/resource.h | 1 + sfall/skills.cpp | 300 +++ sfall/skills.h | 23 + sfall/sound.cpp | 90 + sfall/sound.h | 3 + sfall/timer.cpp | 116 ++ sfall/timer.h | 23 + sfall/vector9x.cpp | 54 + sfall/version.h | 34 + sfall/version.rc | 23 + 127 files changed, 32062 insertions(+) create mode 100644 MPClient/DebugEditor.Designer.cs create mode 100644 MPClient/DebugEditor.cs create mode 100644 MPClient/DebugEditor.resx create mode 100644 MPClient/EditorConnection.cs create mode 100644 MPClient/EditorWindow.Designer.cs create mode 100644 MPClient/EditorWindow.cs create mode 100644 MPClient/EditorWindow.resx create mode 100644 MPClient/MPClient.csproj create mode 100644 MPClient/MPClient.sln create mode 100644 MPClient/Program.cs create mode 100644 MPClient/Properties/AssemblyInfo.cs create mode 100644 sfall/.editorconfig create mode 100644 sfall/AI.cpp create mode 100644 sfall/AI.h create mode 100644 sfall/AmmoMod.cpp create mode 100644 sfall/AmmoMod.h create mode 100644 sfall/AnimationsAtOnceLimit.cpp create mode 100644 sfall/AnimationsAtOnceLimit.h create mode 100644 sfall/Arrays.cpp create mode 100644 sfall/Arrays.h create mode 100644 sfall/BarBoxes.cpp create mode 100644 sfall/BarBoxes.h create mode 100644 sfall/Books.cpp create mode 100644 sfall/Books.h create mode 100644 sfall/BurstMods.cpp create mode 100644 sfall/BurstMods.h create mode 100644 sfall/CRC.cpp create mode 100644 sfall/CRC.h create mode 100644 sfall/Console.cpp create mode 100644 sfall/Console.h create mode 100644 sfall/Credits.cpp create mode 100644 sfall/Credits.h create mode 100644 sfall/Criticals.cpp create mode 100644 sfall/Criticals.h create mode 100644 sfall/DebugEditor.cpp create mode 100644 sfall/DebugEditor.h create mode 100644 sfall/Define.h create mode 100644 sfall/Elevators.cpp create mode 100644 sfall/Elevators.h create mode 100644 sfall/Explosions.cpp create mode 100644 sfall/Explosions.h create mode 100644 sfall/FalloutEngine.cpp create mode 100644 sfall/FalloutEngine.h create mode 100644 sfall/FalloutStructs.h create mode 100644 sfall/FileSystem.cpp create mode 100644 sfall/FileSystem.h create mode 100644 sfall/Graphics.h create mode 100644 sfall/HeroAppearance.cpp create mode 100644 sfall/HeroAppearance.h create mode 100644 sfall/HookScripts.cpp create mode 100644 sfall/HookScripts.h create mode 100644 sfall/Imports.cpp create mode 100644 sfall/Imports.h create mode 100644 sfall/Inventory.cpp create mode 100644 sfall/Inventory.h create mode 100644 sfall/KillCounter.cpp create mode 100644 sfall/KillCounter.h create mode 100644 sfall/Knockback.cpp create mode 100644 sfall/Knockback.h create mode 100644 sfall/LoadGameHook.cpp create mode 100644 sfall/LoadGameHook.h create mode 100644 sfall/Logging.cpp create mode 100644 sfall/Logging.h create mode 100644 sfall/MainMenu.cpp create mode 100644 sfall/MainMenu.h create mode 100644 sfall/Numbers.cpp create mode 100644 sfall/Numbers.h create mode 100644 sfall/PartyControl.cpp create mode 100644 sfall/PartyControl.h create mode 100644 sfall/Perks.cpp create mode 100644 sfall/Perks.h create mode 100644 sfall/Premade.cpp create mode 100644 sfall/Premade.h create mode 100644 sfall/QuestList.cpp create mode 100644 sfall/QuestList.h create mode 100644 sfall/Reputations.cpp create mode 100644 sfall/Reputations.h create mode 100644 sfall/SafeWrite.cpp create mode 100644 sfall/SafeWrite.h create mode 100644 sfall/ScriptExtender.cpp create mode 100644 sfall/ScriptExtender.h create mode 100644 sfall/ScriptOps/AnimOps.hpp create mode 100644 sfall/ScriptOps/AsmMacros.h create mode 100644 sfall/ScriptOps/FileSystemOps.hpp create mode 100644 sfall/ScriptOps/GraphicsOp.hpp create mode 100644 sfall/ScriptOps/InterfaceOp.hpp create mode 100644 sfall/ScriptOps/MemoryOp.hpp create mode 100644 sfall/ScriptOps/MiscOps.hpp create mode 100644 sfall/ScriptOps/ObjectsOps.hpp create mode 100644 sfall/ScriptOps/PerksOp.hpp create mode 100644 sfall/ScriptOps/ScriptArrays.hpp create mode 100644 sfall/ScriptOps/ScriptUtils.hpp create mode 100644 sfall/ScriptOps/StatsOp.hpp create mode 100644 sfall/ScriptOps/WorldmapOps.hpp create mode 100644 sfall/ScriptOps/readme.txt create mode 100644 sfall/Stats.cpp create mode 100644 sfall/Stats.h create mode 100644 sfall/SuperSave.cpp create mode 100644 sfall/SuperSave.h create mode 100644 sfall/Tiles.cpp create mode 100644 sfall/Tiles.h create mode 100644 sfall/ddraw.cpp create mode 100644 sfall/ddraw.sln create mode 100644 sfall/ddraw.vcxproj create mode 100644 sfall/ddraw.vcxproj.filters create mode 100644 sfall/ddraw2.cpp create mode 100644 sfall/dinput.cpp create mode 100644 sfall/exports.def create mode 100644 sfall/heads.cpp create mode 100644 sfall/heads.h create mode 100644 sfall/input.h create mode 100644 sfall/main.cpp create mode 100644 sfall/main.h create mode 100644 sfall/movies.cpp create mode 100644 sfall/movies.h create mode 100644 sfall/queue9x.cpp create mode 100644 sfall/resource.h create mode 100644 sfall/skills.cpp create mode 100644 sfall/skills.h create mode 100644 sfall/sound.cpp create mode 100644 sfall/sound.h create mode 100644 sfall/timer.cpp create mode 100644 sfall/timer.h create mode 100644 sfall/vector9x.cpp create mode 100644 sfall/version.h create mode 100644 sfall/version.rc diff --git a/.gitignore b/.gitignore index b8bd0267..d5792d4c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,197 @@ *.exe *.out *.app + +# NetBeans +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml +.nb-gradle/ + + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt \ No newline at end of file diff --git a/MPClient/DebugEditor.Designer.cs b/MPClient/DebugEditor.Designer.cs new file mode 100644 index 00000000..1ecd8fc4 --- /dev/null +++ b/MPClient/DebugEditor.Designer.cs @@ -0,0 +1,182 @@ +namespace FalloutClient { + partial class DebugEditor { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.bGlobals = new System.Windows.Forms.Button(); + this.bMapVars = new System.Windows.Forms.Button(); + this.bCritters = new System.Windows.Forms.Button(); + this.bSGlobals = new System.Windows.Forms.Button(); + this.bArrays = new System.Windows.Forms.Button(); + this.Column0 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.bEdit = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + this.SuspendLayout(); + // + // dataGridView1 + // + this.dataGridView1.AllowUserToAddRows = false; + this.dataGridView1.AllowUserToDeleteRows = false; + this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.Column0, + this.Column1, + this.Column2, + this.Column3}); + this.dataGridView1.Location = new System.Drawing.Point(12, 12); + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.Size = new System.Drawing.Size(444, 215); + this.dataGridView1.TabIndex = 0; + this.dataGridView1.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellEndEdit); + // + // bGlobals + // + this.bGlobals.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bGlobals.Location = new System.Drawing.Point(12, 233); + this.bGlobals.Name = "bGlobals"; + this.bGlobals.Size = new System.Drawing.Size(100, 23); + this.bGlobals.TabIndex = 1; + this.bGlobals.Text = "Globals"; + this.bGlobals.UseVisualStyleBackColor = true; + this.bGlobals.Click += new System.EventHandler(this.bGlobals_Click); + // + // bMapVars + // + this.bMapVars.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bMapVars.Location = new System.Drawing.Point(118, 233); + this.bMapVars.Name = "bMapVars"; + this.bMapVars.Size = new System.Drawing.Size(100, 23); + this.bMapVars.TabIndex = 2; + this.bMapVars.Text = "Map variables"; + this.bMapVars.UseVisualStyleBackColor = true; + this.bMapVars.Click += new System.EventHandler(this.bMapVars_Click); + // + // bCritters + // + this.bCritters.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bCritters.Location = new System.Drawing.Point(118, 262); + this.bCritters.Name = "bCritters"; + this.bCritters.Size = new System.Drawing.Size(100, 23); + this.bCritters.TabIndex = 3; + this.bCritters.Text = "Critters"; + this.bCritters.UseVisualStyleBackColor = true; + this.bCritters.Click += new System.EventHandler(this.bCritters_Click); + // + // bSGlobals + // + this.bSGlobals.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bSGlobals.Location = new System.Drawing.Point(225, 233); + this.bSGlobals.Name = "bSGlobals"; + this.bSGlobals.Size = new System.Drawing.Size(100, 23); + this.bSGlobals.TabIndex = 4; + this.bSGlobals.Text = "sfall globals"; + this.bSGlobals.UseVisualStyleBackColor = true; + this.bSGlobals.Click += new System.EventHandler(this.bSGlobals_Click); + // + // bArrays + // + this.bArrays.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bArrays.Location = new System.Drawing.Point(12, 262); + this.bArrays.Name = "bArrays"; + this.bArrays.Size = new System.Drawing.Size(100, 23); + this.bArrays.TabIndex = 5; + this.bArrays.Text = "sfall arrays"; + this.bArrays.UseVisualStyleBackColor = true; + this.bArrays.Click += new System.EventHandler(this.bArrays_Click); + // + // Column0 + // + this.Column0.HeaderText = "ID"; + this.Column0.Name = "Column0"; + this.Column0.ReadOnly = true; + // + // Column1 + // + this.Column1.HeaderText = "Name"; + this.Column1.Name = "Column1"; + this.Column1.ReadOnly = true; + // + // Column2 + // + this.Column2.HeaderText = "Value (Int)"; + this.Column2.Name = "Column2"; + // + // Column3 + // + this.Column3.HeaderText = "Value (Float)"; + this.Column3.Name = "Column3"; + // + // bEdit + // + this.bEdit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bEdit.Location = new System.Drawing.Point(225, 262); + this.bEdit.Name = "bEdit"; + this.bEdit.Size = new System.Drawing.Size(100, 23); + this.bEdit.TabIndex = 6; + this.bEdit.Text = "Edit"; + this.bEdit.UseVisualStyleBackColor = true; + this.bEdit.Click += new System.EventHandler(this.bEdit_Click); + // + // DebugEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(468, 297); + this.Controls.Add(this.bEdit); + this.Controls.Add(this.bArrays); + this.Controls.Add(this.bSGlobals); + this.Controls.Add(this.bCritters); + this.Controls.Add(this.bMapVars); + this.Controls.Add(this.bGlobals); + this.Controls.Add(this.dataGridView1); + this.Name = "DebugEditor"; + this.Text = "DebugEditor"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DebugEditor_FormClosing); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.Button bGlobals; + private System.Windows.Forms.Button bMapVars; + private System.Windows.Forms.Button bCritters; + private System.Windows.Forms.Button bSGlobals; + private System.Windows.Forms.Button bArrays; + private System.Windows.Forms.DataGridViewTextBoxColumn Column0; + private System.Windows.Forms.DataGridViewTextBoxColumn Column1; + private System.Windows.Forms.DataGridViewTextBoxColumn Column2; + private System.Windows.Forms.DataGridViewTextBoxColumn Column3; + private System.Windows.Forms.Button bEdit; + + } +} \ No newline at end of file diff --git a/MPClient/DebugEditor.cs b/MPClient/DebugEditor.cs new file mode 100644 index 00000000..d419bfb6 --- /dev/null +++ b/MPClient/DebugEditor.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using File=System.IO.File; +using StreamReader=System.IO.StreamReader; +using MemoryStream=System.IO.MemoryStream; +using BinaryReader=System.IO.BinaryReader; +using BinaryWriter=System.IO.BinaryWriter; + +namespace FalloutClient { + public partial class DebugEditor : Form { + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)] + private struct ByteConverter { + [System.Runtime.InteropServices.FieldOffset(0)] + private int i; + [System.Runtime.InteropServices.FieldOffset(0)] + private float f; + + public int GetAsInt(float f) { + this.f=f; + return i; + } + public float GetAsFloat(int i) { + this.i=i; + return f; + } + public byte[] GetAsBytes(ulong ul) { + byte[] b=new byte[8]; + for(int i=0;i<8;i++) b[i]=(byte)((ul&((ulong)0xff<<(i*8)))>>(i*8)); + return b; + } + } + + private static ByteConverter converter=new ByteConverter(); + + private enum Mode { Globals, MapVars, SGlobals, Arrays, Critters } + + private readonly EditorConnection connection; + private Mode mode; + + private readonly Dictionary GlobNames=new Dictionary(); + private readonly Dictionary CritNames=new Dictionary(); + + private void Redraw() { + dataGridView1.SuspendLayout(); + dataGridView1.Rows.Clear(); + switch(mode) { + case Mode.Globals: + Column2.ReadOnly=false; + Column3.ReadOnly=false; + Column2.HeaderText="Value (Int)"; + Column3.HeaderText="Value (Float)"; + bEdit.Enabled=false; + for(int i=0;i0xffffffff?new string(System.Text.ASCIIEncoding.ASCII.GetChars(converter.GetAsBytes(connection.SGlobalKeys[i]))):("0x"+connection.SGlobalKeys[i].ToString("x")); + dataGridView1.Rows.Add(s, "", connection.sGlobals[i], converter.GetAsFloat(connection.sGlobals[i])); + } + break; + case Mode.Arrays: + Column2.ReadOnly=true; + Column3.ReadOnly=true; + Column2.HeaderText="Array length"; + Column3.HeaderText="Element size"; + bEdit.Enabled=true; + for(int i=0;i(bytes, 0)); + break; + } + } + br.Close(); + strings=EditorWindow.ShowEditor(null, types, strings); + if(strings!=null) { + connection.WriteDataType(DataTypeSend.SetArray); + connection.WriteInt(i); + ms=new MemoryStream(connection.ArrayLengths[i]*connection.ArrayDataSizes[i]); + BinaryWriter bw=new BinaryWriter(ms); + for(int j=0;j + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/MPClient/EditorConnection.cs b/MPClient/EditorConnection.cs new file mode 100644 index 00000000..669fecd3 --- /dev/null +++ b/MPClient/EditorConnection.cs @@ -0,0 +1,103 @@ +using System; +using System.Net.Sockets; +using System.IO; + +namespace FalloutClient { + class EditorConnection { + private TcpClient client; + private NetworkStream stream; + private BinaryReader br; + private BinaryWriter bw; + + public readonly int[] Globals; + public readonly int[] MapVars; + public readonly uint[] Critters; + public readonly ulong[] SGlobalKeys; + public readonly int[] sGlobals; + public readonly uint[] Arrays; + public readonly int[] ArrayLengths; + public readonly int[] ArrayDataSizes; + + public EditorConnection() { + System.Threading.Thread.Sleep(1000); + client=new TcpClient("127.0.0.1", 4245); + stream=client.GetStream(); + br=new BinaryReader(stream); + bw=new BinaryWriter(stream); + Globals=new int[br.ReadInt32()]; + MapVars=new int[br.ReadInt32()]; + SGlobalKeys=new ulong[br.ReadInt32()]; + sGlobals=new int[SGlobalKeys.Length]; + Arrays=new uint[br.ReadInt32()]; + ArrayLengths=new int[Arrays.Length]; + ArrayDataSizes=new int[Arrays.Length]; + Critters=new uint[br.ReadInt32()]; + + for(int i=0;i + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.bSave = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + this.SuspendLayout(); + // + // bSave + // + this.bSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bSave.Location = new System.Drawing.Point(281, 265); + this.bSave.Name = "bSave"; + this.bSave.Size = new System.Drawing.Size(75, 23); + this.bSave.TabIndex = 0; + this.bSave.Text = "Save"; + this.bSave.UseVisualStyleBackColor = true; + this.bSave.Click += new System.EventHandler(this.bSave_Click); + // + // bCancel + // + this.bCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bCancel.Location = new System.Drawing.Point(200, 265); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size(75, 23); + this.bCancel.TabIndex = 1; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + this.bCancel.Click += new System.EventHandler(this.bCancel_Click); + // + // dataGridView1 + // + this.dataGridView1.AllowUserToAddRows = false; + this.dataGridView1.AllowUserToDeleteRows = false; + this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.Column1, + this.Column2, + this.Column3}); + this.dataGridView1.Location = new System.Drawing.Point(12, 12); + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.Size = new System.Drawing.Size(344, 247); + this.dataGridView1.TabIndex = 2; + this.dataGridView1.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellEndEdit); + // + // Column1 + // + this.Column1.HeaderText = "Name"; + this.Column1.Name = "Column1"; + this.Column1.ReadOnly = true; + // + // Column2 + // + this.Column2.HeaderText = "Type"; + this.Column2.Name = "Column2"; + this.Column2.ReadOnly = true; + // + // Column3 + // + this.Column3.HeaderText = "Value"; + this.Column3.Name = "Column3"; + // + // EditorWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(368, 300); + this.Controls.Add(this.dataGridView1); + this.Controls.Add(this.bCancel); + this.Controls.Add(this.bSave); + this.Name = "EditorWindow"; + this.Text = "EditorWindow"; + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button bSave; + private System.Windows.Forms.Button bCancel; + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.DataGridViewTextBoxColumn Column1; + private System.Windows.Forms.DataGridViewTextBoxColumn Column2; + private System.Windows.Forms.DataGridViewTextBoxColumn Column3; + } +} \ No newline at end of file diff --git a/MPClient/EditorWindow.cs b/MPClient/EditorWindow.cs new file mode 100644 index 00000000..9e514192 --- /dev/null +++ b/MPClient/EditorWindow.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace FalloutClient { + public enum DataType : int { None=0, Int=1, Float=2, String=3 } + public partial class EditorWindow : Form { + private readonly DataType[] types; + private readonly string[] values; + private bool save; + + private EditorWindow(string[] names, DataType[] types, string[] values) { + this.types=types; + this.values=values; + InitializeComponent(); + dataGridView1.SuspendLayout(); + if(names==null) for(int i=0;i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/MPClient/MPClient.csproj b/MPClient/MPClient.csproj new file mode 100644 index 00000000..ab9f78f5 --- /dev/null +++ b/MPClient/MPClient.csproj @@ -0,0 +1,76 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {80ED0DCE-100E-4EC9-A79D-2FBFA4CAD1E3} + WinExe + Properties + FalloutClient + FalloutClient + + + 3.5 + + + v2.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + Form + + + DebugEditor.cs + + + + Form + + + EditorWindow.cs + + + + + DebugEditor.cs + + + EditorWindow.cs + + + + + + copy "$(TargetPath)" "C:\Games\Fallout2\$(TargetName)$(TargetExt)" + + \ No newline at end of file diff --git a/MPClient/MPClient.sln b/MPClient/MPClient.sln new file mode 100644 index 00000000..14e5dc69 --- /dev/null +++ b/MPClient/MPClient.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MPClient", "MPClient.csproj", "{80ED0DCE-100E-4EC9-A79D-2FBFA4CAD1E3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {80ED0DCE-100E-4EC9-A79D-2FBFA4CAD1E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80ED0DCE-100E-4EC9-A79D-2FBFA4CAD1E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80ED0DCE-100E-4EC9-A79D-2FBFA4CAD1E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80ED0DCE-100E-4EC9-A79D-2FBFA4CAD1E3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MPClient/Program.cs b/MPClient/Program.cs new file mode 100644 index 00000000..73f84324 --- /dev/null +++ b/MPClient/Program.cs @@ -0,0 +1,28 @@ +using System; +using System.Windows.Forms; +using System.IO; + +namespace FalloutClient { + static class Program { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + if(args.Length==1&&args[0]=="-debugedit") { + Application.Run(new DebugEditor(new EditorConnection())); + } else { + throw new Exception("Multiplayer is no longer supported"); + } + } + } + + enum DataTypeSend { + Mouse=0, LMouseDown=1, LMouseUp=2, RMouseDown=3, RMouseUp=4, TakeChar=5, ReleaseChar=6, + SetGlobal=0, SetMapVar=1, RetrieveCritter=2, SetCritter=3, SetSGlobal=4, GetArray=9, SetArray=10, + Exit=254 + } +} diff --git a/MPClient/Properties/AssemblyInfo.cs b/MPClient/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..85abd150 --- /dev/null +++ b/MPClient/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FalloutClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FalloutClient")] +[assembly: AssemblyCopyright("Copyright © 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6b3c8cd6-54f0-4f35-a87e-50f760ad36fd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sfall/.editorconfig b/sfall/.editorconfig new file mode 100644 index 00000000..68c66184 --- /dev/null +++ b/sfall/.editorconfig @@ -0,0 +1,15 @@ +; Top-most EditorConfig file +root = true + +; 4-column tab indentation +[*.cpp] +indent_style = tab +indent_size = 4 + +[*.h] +indent_style = tab +indent_size = 4 + +[*.hpp] +indent_style = tab +indent_size = 4 \ No newline at end of file diff --git a/sfall/AI.cpp b/sfall/AI.cpp new file mode 100644 index 00000000..f45f58e6 --- /dev/null +++ b/sfall/AI.cpp @@ -0,0 +1,126 @@ +/* + * sfall + * Copyright (C) 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" +#include "AI.h" +#include "SafeWrite.h" +#include "FalloutEngine.h" + +#include + +typedef stdext::hash_map :: const_iterator iter; + +static stdext::hash_map targets; +static stdext::hash_map sources; + +DWORD _stdcall AIGetLastAttacker(DWORD target) { + iter itr=sources.find(target); + if(itr==sources.end()) return 0; + else return itr->second; +} + +DWORD _stdcall AIGetLastTarget(DWORD source) { + iter itr=targets.find(source); + if(itr==targets.end()) return 0; + else return itr->second; +} + +static void _stdcall CombatAttackHook(DWORD source, DWORD target) { + sources[target]=source; + targets[source]=target; +} + +//static const DWORD _ai_attack=0x42A748; +static const DWORD _combat_attack=0x422F3C; +static void __declspec(naked) combat_attack_hook() { + _asm { + pushad; + push edx; + push eax; + call CombatAttackHook; + popad; + jmp _combat_attack; + } +} + +static DWORD CombatDisabled; +static char CombatBlockedMessage[128]; + +static void _stdcall CombatBlocked() { + DisplayConsoleMessage(CombatBlockedMessage); +} + +static const DWORD BlockCombatHook1Ret1=0x45F6B4; +static const DWORD BlockCombatHook1Ret2=0x45F6D7; +static void __declspec(naked) BlockCombatHook1() { + __asm { + mov eax, CombatDisabled; + test eax, eax; + jz end; + call CombatBlocked; + jmp BlockCombatHook1Ret2; +end: + mov eax, 0x14; + jmp BlockCombatHook1Ret1; + } +} + +static void __declspec(naked) BlockCombatHook2() { + __asm { + mov eax, dword ptr ds:[0x518F10]; + test eax, eax; + jz end; + mov eax, CombatDisabled; + test eax, eax; + jz succeed; + pushad; + call CombatBlocked; + popad; + xor eax, eax; + jmp end; +succeed: + inc eax; +end: + retn; + } +} + +void _stdcall AIBlockCombat(DWORD i) { + if(i) CombatDisabled=1; + else CombatDisabled=0; +} + +void AIInit() { + //HookCall(0x42AE1D, ai_attack_hook); + //HookCall(0x42AE5C, ai_attack_hook); + HookCall(0x426A95, combat_attack_hook); + HookCall(0x42A796, combat_attack_hook); + MakeCall(0x45F6AF, BlockCombatHook1, true); + HookCall(0x4432A6, BlockCombatHook2); + GetPrivateProfileString("sfall", "BlockedCombat", "You cannot enter combat at this time.", CombatBlockedMessage, 128, translationIni); +} + +void _stdcall AICombatStart() { + targets.clear(); + sources.clear(); +} + +void _stdcall AICombatEnd() { + targets.clear(); + sources.clear(); +} \ No newline at end of file diff --git a/sfall/AI.h b/sfall/AI.h new file mode 100644 index 00000000..d2982744 --- /dev/null +++ b/sfall/AI.h @@ -0,0 +1,26 @@ +/* + * sfall + * Copyright (C) 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +void AIInit(); +void _stdcall AICombatStart(); +void _stdcall AICombatEnd(); + +DWORD _stdcall AIGetLastAttacker(DWORD target); +DWORD _stdcall AIGetLastTarget(DWORD source); + +void _stdcall AIBlockCombat(DWORD i); diff --git a/sfall/AmmoMod.cpp b/sfall/AmmoMod.cpp new file mode 100644 index 00000000..65879965 --- /dev/null +++ b/sfall/AmmoMod.cpp @@ -0,0 +1,784 @@ +/* +* sfall +* Copyright (C) 2008, 2009, 2010, 2013, 2014 The sfall team +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include "main.h" + +#include "Logging.h" + +static const DWORD DamageFunctionSub1 = 0x00478448; +static const DWORD DamageFunctionReturn = 0x00424A63; +static const DWORD GetAmmoDividend = 0x00479230; +static const DWORD GetAmmoDivisor = 0x00479294; +static const DWORD GetAmmoDTMod = 0x004791E0; + +// Damage Fix v5 by Glovz 2014.04.16.xx.xx +static __declspec(naked) void DamageFunction1() { +__asm { + mov ebx,dword ptr ss:[esp+0x1c]; // get the number of hits + xor ecx,ecx; // set the loop counter to 0 + cmp ebx,0x0; // compare the number of hits to 0 + jle end; // exit +begin: + mov dword ptr ss:[esp+0x30],0x0; // clear value + mov edx,dword ptr ds:[esi+4]; // get pointer to weapon being used by an attacker (I think) + mov eax,dword ptr ds:[esi]; // get pointer to critter attacking + call DamageFunctionSub1; // get the raw damage value + mov ebx,dword ptr ss:[esp+0x18]; // get the bonus ranged damage value + cmp ebx,0x0; // compare the range bonus damage value to 0 + jle rdJmp; // if the RB value is less than or equal to 0 then goto rdJmp + add eax,ebx; // add the RB value to the RD value + rdJmp: + cmp eax,0x0; // compare the new damage value to 0 + jle noDamageJmp; // goto noDamageJmp + mov ebx,eax; // set the ND value + mov edx,dword ptr ss:[esp+0x28]; // get the armorDT value + cmp edx,0x0; // compare the armorDT value to 0 + jle bJmp; // if the armorDT value is less than or equal to 0 then goto bJmp + mov eax,dword ptr ds:[esi+0x8]; // get pointer to critters ammo being used in their weapon (I think) + call GetAmmoDivisor; // get the ammoY value + cmp eax,0x0; // compare the ammoY value to 0 + jg aJmp; // if the ammoY value is greater than 0 then goto aJmp + mov eax,0x1; // set the ammoY value to 1 +aJmp: + xor ebp,ebp; // clear value + cmp edx,eax; // compare the dividend with the divisor + jl lrThan; // the dividend is less than the divisor then goto lrThan + jg grThan; // the dividend is greater than the divisor then goto grThan + jmp setOne; // if the two values are equal then goto setOne + lrThan: + mov ebp,edx; // store the dividend value temporarily + imul edx,0x2; // multiply dividend value by 2 + sub edx,eax; // subtract divisor value from the dividend value + cmp edx,0x0; // compare the result to 0 + jl setZero; // if the result is less than 0 then goto setZero + jg setOne; // if the result is greater than 0 then goto setOne + mov edx,ebp; // restore dividend value + and edx,0x1; // if true (1) then odd if false (0) then even + jz setZero; // if the result is equal to 0 then setZero + jmp setOne; // if the result is not 0 then goto setOne + grThan: + mov ebp,eax; // assign the divisor value + xor eax,eax; // clear value + bbbJmp: + inc eax; // add 1 to the quotient value + sub edx,ebp; // subtract the divisor value from the dividend value + cmp edx,ebp; // compare the remainder value to the divisor value + jge bbbJmp; // if the remainder value is greater or equal to the divisor value then goto bbbJmp + jz endDiv; // if the remainder value is equal to 0 then goto endDiv + imul edx,0x2; // multiply temp remainder value by 2 + sub edx,ebp; // subtract the divisor value from the temp remainder + cmp edx,0x0; // compare the result to 0 + jl endDiv; // if the result is less than 0 then goto endDiv + jg addOne; // if the result is greater than 0 then goto addOne + mov ebp,eax; // assign the quotient value + and ebp,0x1; // if true (1) then odd if false (0) then even + jz endDiv; // if the result is equal to zero goto endDiv + addOne: + inc eax; // add 1 to the quotient value + jmp endDiv; // goto endDiv + setOne: + mov eax,0x1; // set the quotient value to 1 + jmp endDiv; // goto endDiv + setZero: + xor eax,eax; // clear value + endDiv: + cmp dword ptr ss:[esp+0x30],0x2; // compare value to 2 + je divTwo; // goto divTwo + cmp dword ptr ss:[esp+0x30],0x3; // compare value to 3 + je divThree; // goto divThree + cmp dword ptr ss:[esp+0x30],0x4; // compare value to 4 + je divFour; // goto divFour + cmp dword ptr ss:[esp+0x30],0x5; // compare value to 5 + je divFive; // goto divFive + cmp dword ptr ss:[esp+0x30],0x6; // compare value to 6 + je divSix; // goto divSix + sub ebx,eax; // subtract the new armorDT value from the RD value + jmp cJmp; // goto cJmp +bJmp: + mov edx,dword ptr ss:[esp+0x2c]; // get the armorDR value + cmp edx,0x0; // compare the armorDR value to 0 + jle dJmp; // if the armorDR value is less than or equal to 0 then goto dJmp +cJmp: + cmp ebx,0x0; // compare the new damage value to 0 + jle noDamageJmp; // goto noDamageJmp + mov edx,dword ptr ss:[esp+0x2c]; // get the armorDR value + cmp edx,0x0; // compare the armorDR value to 0 + jle eJmp; // if the armorDR value is less than or equal to 0 then goto eJmp + mov eax,dword ptr ss:[esp+0x20]; // get the CD value + cmp eax,0x64; // compare the CD value to 100 + jg sdrJmp; // if the CD value is greater than 100 then goto sdrJmp + je aSubCJmp; // if the CD value is equal to 100 then goto aSubCJmp + add edx,0x14; // add 20 to the armorDR value + jmp aSubCJmp; // goto aSubCJmp + sdrJmp: + sub edx,0x14; // subtract 20 from the armorDR value + aSubCJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to ammo being used in the critters weapon (I think) + call GetAmmoDTMod; // get the ammoDRM value + cmp eax,0x0; // compare the ammoDRM value to 0 + jl adrJmp; // if the ammoDRM value is less than 0 then goto adrJmp + je bSubCJmp; // if the ammoDRM value is equal to 0 then goto bSubCJmp + xor ebp,ebp; // clear value + sub ebp,eax; // subtract ammoDRM value from 0 + mov eax,ebp; // set new ammoDRM value + adrJmp: + add edx,eax; // add the ammoDRM value to the armorDR value + bSubCJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to ammo being used in the critters weapon (I think) + call GetAmmoDividend; // get the ammoX value + cmp eax,0x0; // compare the ammoX value to 0 + jg cSubCJmp; // if the ammoX value is greater than 0 then goto cSubCJmp; + mov eax,0x1; // set the ammoX value to 1 + cSubCJmp: + mov dword ptr ss:[esp+0x30],0x2; // set value to 2 + jmp aJmp; // goto aJmp + divTwo: + mov edx,ebx; // set temp value + imul edx,eax; // multiply the ND value by the armorDR value + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x3; // set value to 3 + jmp aJmp; // goto aJmp + divThree: + sub ebx,eax; // subtract the damage resisted value from the ND value + jmp eJmp; // goto eJmp +dJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to ammo being used in the critters weapon (I think) + call GetAmmoDividend; // get the ammoX value + cmp eax,0x1; // compare the ammoX value to 1 + jle bSubDJmp; // if the ammoX value is less than or equal to 1 then goto bSubDJmp; + mov eax,dword ptr ds:[esi+0x8]; // get pointer to critters ammo being used in their weapon (I think) + call GetAmmoDivisor; // get the ammoY value + cmp eax,0x1; // compare the ammoY value to 1 + jle aSubDJmp; // if the ammoY value is less than or equal to 1 then goto aSubDJmp + mov edx,ebx; // set temp value + imul edx,0xf; // multiply the ND value by 15 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x4; // set value to 4 + jmp aJmp; // goto aJmp + divFour: + add ebx,eax; // add the quotient value to the ND value + jmp eJmp; // goto eJmp + aSubDJmp: + mov edx,ebx; // set temp value + imul edx,0x14; // multiply the ND value by 20 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x5; // set value to 5 + jmp aJmp; // goto aJmp + divFive: + add ebx,eax; // add the quotient value to the ND value + jmp eJmp; // goto eJmp + bSubDJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to critters ammo being used in their weapon (I think) + call GetAmmoDivisor; // get the ammoY value + cmp eax,0x1; // compare the ammoY value to 1 + jle eJmp; // goto eJmp + mov edx,ebx; // set temp value + imul edx,0xa; // multiply the ND value by 10 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x6; // set value to 6 + jmp aJmp; // goto aJmp + divSix: + add ebx,eax; // add the quotient value to the ND value +eJmp: + cmp ebx,0x0; // compare the new damage value to 0 + jle noDamageJmp; // goto noDamageJmp + mov eax,dword ptr ss:[esp+0x24]; // get the CM value + cmp eax,0x2; // compare the CM value to 2 + jle addNDJmp; // if the CM value is less than or equal to 2 then goto addNDJmp + imul ebx,eax; // multiply the ND value by the CM value + sar ebx,0x1; // divide the result by 2 +addNDJmp: + add dword ptr ds:[edi],ebx; // accumulate damage +noDamageJmp: + mov eax,dword ptr ss:[esp+0x1c]; // get the number of hits + inc ecx; // the counter value increments by one + cmp ecx,eax; // is the counter value less than the number of hits + jl begin; // goto begin +end: + jmp DamageFunctionReturn; // exit + } +} + +// Damage Fix v5.1 by Glovz 2014.04.16.xx.xx +static __declspec(naked) void DamageFunction2() { +__asm { + mov ebx,dword ptr ss:[esp+0x1c]; // get the number of hits + xor ecx,ecx; // set the loop counter to 0 + cmp ebx,0x0; // compare the number of hits to 0 + jle end; // exit +begin: + mov dword ptr ss:[esp+0x30],0x0; // clear value + mov edx,dword ptr ds:[esi+4]; // get pointer to weapon being used by an attacker (I think) + mov eax,dword ptr ds:[esi]; // get pointer to critter attacking + call DamageFunctionSub1; // get the raw damage value + mov ebx,dword ptr ss:[esp+0x18]; // get the bonus ranged damage value + cmp ebx,0x0; // compare the range bonus damage value to 0 + jle rdJmp; // if the RB value is less than or equal to 0 then goto rdJmp + add eax,ebx; // add the RB value to the RD value + rdJmp: + cmp eax,0x0; // compare the new damage value to 0 + jle noDamageJmp; // goto noDamageJmp + mov ebx,eax; // set the ND value + mov edx,dword ptr ss:[esp+0x28]; // get the armorDT value + cmp edx,0x0; // compare the armorDT value to 0 + jle bJmp; // if the armorDT value is less than or equal to 0 then goto bJmp + mov eax,dword ptr ds:[esi+0x8]; // get pointer to critters ammo being used in their weapon (I think) + call GetAmmoDivisor; // get the ammoY value + cmp eax,0x0; // compare the ammoY value to 0 + jg aJmp; // if the ammoY value is greater than 0 then goto aJmp + mov eax,0x1; // set the ammoY value to 1 +aJmp: + xor ebp,ebp; // clear value + cmp edx,eax; // compare the dividend with the divisor + jl lrThan; // the dividend is less than the divisor then goto lrThan + jg grThan; // the dividend is greater than the divisor then goto grThan + jmp setOne; // if the two values are equal then goto setOne + lrThan: + mov ebp,edx; // store the dividend value temporarily + imul edx,0x2; // multiply dividend value by 2 + sub edx,eax; // subtract divisor value from the dividend value + cmp edx,0x0; // compare the result to 0 + jl setZero; // if the result is less than 0 then goto setZero + jg setOne; // if the result is greater than 0 then goto setOne + mov edx,ebp; // restore dividend value + and edx,0x1; // if true (1) then odd if false (0) then even + jz setZero; // if the result is equal to 0 then setZero + jmp setOne; // if the result is not 0 then goto setOne + grThan: + mov ebp,eax; // assign the divisor value + xor eax,eax; // clear value + bbbJmp: + inc eax; // add 1 to the quotient value + sub edx,ebp; // subtract the divisor value from the dividend value + cmp edx,ebp; // compare the remainder value to the divisor value + jge bbbJmp; // if the remainder value is greater or equal to the divisor value then goto bbbJmp + jz endDiv; // if the remainder value is equal to 0 then goto endDiv + imul edx,0x2; // multiply temp remainder value by 2 + sub edx,ebp; // subtract the divisor value from the temp remainder + cmp edx,0x0; // compare the result to 0 + jl endDiv; // if the result is less than 0 then goto endDiv + jg addOne; // if the result is greater than 0 then goto addOne + mov ebp,eax; // assign the quotient value + and ebp,0x1; // if true (1) then odd if false (0) then even + jz endDiv; // if the result is equal to zero goto endDiv + addOne: + inc eax; // add 1 to the quotient value + jmp endDiv; // goto endDiv + setOne: + mov eax,0x1; // set the quotient value to 1 + jmp endDiv; // goto endDiv + setZero: + xor eax,eax; // clear value + endDiv: + cmp dword ptr ss:[esp+0x30],0x2; // compare value to 2 + je divTwo; // goto divTwo + cmp dword ptr ss:[esp+0x30],0x3; // compare value to 3 + je divThree; // goto divThree + cmp dword ptr ss:[esp+0x30],0x4; // compare value to 4 + je divFour; // goto divFour + cmp dword ptr ss:[esp+0x30],0x5; // compare value to 5 + je divFive; // goto divFive + cmp dword ptr ss:[esp+0x30],0x6; // compare value to 6 + je divSix; // goto divSix + cmp dword ptr ss:[esp+0x30],0x7; // compare value to 7 + je divSeven; // goto divSeven + sub ebx,eax; // subtract the new armorDT value from the RD value + jmp cJmp; // goto cJmp +bJmp: + mov edx,dword ptr ss:[esp+0x2c]; // get the armorDR value + cmp edx,0x0; // compare the armorDR value to 0 + jle dJmp; // if the armorDR value is less than or equal to 0 then goto dJmp +cJmp: + cmp ebx,0x0; // compare the new damage value to 0 + jle noDamageJmp; // goto noDamageJmp + mov edx,dword ptr ss:[esp+0x2c]; // get the armorDR value + cmp edx,0x0; // compare the armorDR value to 0 + jle eJmp; // if the armorDR value is less than or equal to 0 then goto eJmp + mov eax,dword ptr ss:[esp+0x20]; // get the CD value + cmp eax,0x64; // compare the CD value to 100 + jg sdrJmp; // if the CD value is greater than 100 then goto sdrJmp + je aSubCJmp; // if the CD value is equal to 100 then goto aSubCJmp + add edx,0x14; // add 20 to the armorDR value + jmp aSubCJmp; // goto aSubCJmp + sdrJmp: + sub edx,0x14; // subtract 20 from the armorDR value + aSubCJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to ammo being used in the critters weapon (I think) + call GetAmmoDTMod; // get the ammoDRM value + cmp eax,0x0; // compare the ammoDRM value to 0 + jl adrJmp; // if the ammoDRM value is less than 0 then goto adrJmp + je bSubCJmp; // if the ammoDRM value is equal to 0 then goto bSubCJmp + xor ebp,ebp; // clear value + sub ebp,eax; // subtract ammoDRM value from 0 + mov eax,ebp; // set new ammoDRM value + adrJmp: + add edx,eax; // add the ammoDRM value to the armorDR value + bSubCJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to ammo being used in the critters weapon (I think) + call GetAmmoDividend; // get the ammoX value + cmp eax,0x0; // compare the ammoX value to 0 + jg cSubCJmp; // if the ammoX value is greater than 0 then goto cSubCJmp; + mov eax,0x1; // set the ammoX value to 1 + cSubCJmp: + mov dword ptr ss:[esp+0x30],0x2; // set value to 2 + jmp aJmp; // goto aJmp + divTwo: + mov edx,ebx; // set temp value + imul edx,eax; // multiply the ND value by the armorDR value + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x3; // set value to 3 + jmp aJmp; // goto aJmp + divThree: + sub ebx,eax; // subtract the damage resisted value from the ND value + jmp eJmp; // goto eJmp +dJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to ammo being used in the critters weapon (I think) + call GetAmmoDividend; // get the ammoX value + cmp eax,0x1; // compare the ammoX value to 1 + jle bSubDJmp; // if the ammoX value is less than or equal to 1 then goto bSubDJmp; + mov eax,dword ptr ds:[esi+0x8]; // get pointer to critters ammo being used in their weapon (I think) + call GetAmmoDivisor; // get the ammoY value + cmp eax,0x1; // compare the ammoY value to 1 + jle aSubDJmp; // if the ammoY value is less than or equal to 1 then goto aSubDJmp + mov edx,ebx; // set temp value + imul edx,0xf; // multiply the ND value by 15 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x4; // set value to 4 + jmp aJmp; // goto aJmp + divFour: + add ebx,eax; // add the quotient value to the ND value + jmp eJmp; // goto eJmp + aSubDJmp: + mov edx,ebx; // set temp value + imul edx,0x14; // multiply the ND value by 20 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x5; // set value to 5 + jmp aJmp; // goto aJmp + divFive: + add ebx,eax; // add the quotient value to the ND value + jmp eJmp; // goto eJmp + bSubDJmp: + mov eax,dword ptr ds:[esi+0x8]; // get pointer to critters ammo being used in their weapon (I think) + call GetAmmoDivisor; // get the ammoY value + cmp eax,0x1; // compare the ammoY value to 1 + jle eJmp; // goto eJmp + mov edx,ebx; // set temp value + imul edx,0xa; // multiply the ND value by 10 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x6; // set value to 6 + jmp aJmp; // goto aJmp + divSix: + add ebx,eax; // add the quotient value to the ND value +eJmp: + cmp ebx,0x0; // compare the new damage value to 0 + jle noDamageJmp; // goto noDamageJmp + mov eax,dword ptr ss:[esp+0x24]; // get the CM value + cmp eax,0x2; // compare the CM value to 2 + jle addNDJmp; // if the CM value is less than or equal to 2 then goto addNDJmp + mov edx,ebx; // set temp ND value + imul edx,eax; // multiply the temp ND value by the CM value + imul edx,0x19; // multiply the temp ND value by 25 + mov eax,0x64; // set divisor value to 100 + mov dword ptr ss:[esp+0x30],0x7; // set value to 7 + jmp aJmp; // goto aJmp + divSeven: + add ebx,eax; // add the critical damage value to the ND value +addNDJmp: + add dword ptr ds:[edi],ebx; // accumulate damage +noDamageJmp: + mov eax,dword ptr ss:[esp+0x1c]; // get the number of hits + inc ecx; // the counter value increments by one + cmp ecx,eax; // is the counter value less than the number of hits + jl begin; // goto begin +end: + jmp DamageFunctionReturn; // exit + } +} + +// Jim's Formula +static __declspec(naked) void DamageFunction4() { + __asm { + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + call GetAmmoDivisor; // Retrieve Ammo Divisor + imul ebp,eax; // Ammo Divisor = 1 * Ammo Divisor (ebp set to 1 earlier in function) + mov ebx,dword ptr ss:[esp+0x1c]; // Get number of hits + xor ecx,ecx; // Set loop counter to zero + test ebx,ebx; // Is number of hits smaller than= 0? + jle end; // If yes, jump beyond damage calculation loop +ajmp: // Start of damage calculation loop + mov edx,dword ptr ds:[esi+0x4]; // Get pointer to weapon (?) + mov eax,dword ptr ds:[esi]; // Get pointer to critter (?) + call DamageFunctionSub1; // Retrieve Raw Damage + mov ebx,eax; // Move Raw Damage to ebx + mov edx,dword ptr ss:[esp+0x28]; // Get armor DT + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + call GetAmmoDTMod; // Retrieve ammo AM (Armor Modifier: adds or removes a percentage of the DT and DR) + cmp edx,eax; // is ammo DT bigger than AM + jge ijmp; // If yes, go to end of calc because threshold not met + mov edx,dword ptr ss:[esp+0x2c]; // Get armor DR + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + call GetAmmoDTMod; // Retrieve ammo AM + imul edx,eax; // DR modifier = armor DR * ammo AM + mov dword ptr ss:[esp+0x30],0x64; //sets some variable to 100 + mov eax,edx; //(not sure if I have to do this, but Haen does it) + sar edx,0x1f; // makes DR modifier the dividend + idiv dword ptr ss:[esp+0x30]; // DR modifier = DR modifier / 100 + mov edx,dword ptr ss:[esp+0x2c]; // Get armor DR + sub edx,eax; // DR = DR - DR modifier (can be negative) + test edx,edx; // Is DR >= 0? + jge gjmp; + xor edx,edx; // If no, set DR = 0 + jmp hjmp; +gjmp: + cmp edx,0x64; // Otherwise, is DR bigger than or equal to 100? + jge ijmp; // If yes, damage will be zero, so stop calculating and go to bottom of loop +hjmp: + imul edx,ebx; // Otherwise, Resisted Damage = DR * Raw Damage + mov dword ptr ss:[esp+0x30],0x64; + mov eax,edx; + sar edx,0x1f; + idiv dword ptr ss:[esp+0x30]; // Resisted Damage = Resisted Damage / 100 + sub ebx,eax; // Raw Damage = Raw Damage - Resisted Damage + test ebx,ebx; // Is Raw Damage smaller than 0? + jle ijmp; // If yes, don't accumulate damage + add dword ptr ds:[edi],ebx; // Otherwise, Accumulated Damage = Accumulated Damage + Raw Damage +ijmp: + mov eax,dword ptr ss:[esp+0x1c]; // Get number of hits + inc ecx; // counter += 1 + cmp ecx,eax; // Is counter smaller than number of hits? + jl ajmp; // If yes, go back to start of damage calcuation loop (calculate damage for next hit) +end: + jmp DamageFunctionReturn; // Otherwise, exit loop + } +} + +// YAAM +static __declspec(naked) void DamageFunction5() { + __asm { + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + mov edx,dword ptr ss:[esp+0x24]; // Get Critical Multiplier (passed in as argument to function) + call GetAmmoDividend; // Retrieve Ammo Dividend + imul edx,eax; // Damage Multipler = Critical Multipler * Ammo Dividend + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + call GetAmmoDivisor; // Retrieve Ammo Divisor + imul ebp,eax; // Ammo Divisor = 1 * Ammo Divisor (ebp set to 1 earlier in function) + mov ebx,dword ptr ss:[esp+0x1c]; // Get number of hits + xor ecx,ecx; // Set loop counter to zero + mov dword ptr ss:[esp+0x24],edx; // Store Damage Multiplier + test ebx,ebx; // Is number of hits <= 0? + jle end; // If yes, jump beyond damage calculation loop +ajmp: // Start of damage calculation loop + mov edx,dword ptr ds:[esi+0x4]; // Get pointer to weapon (?) + mov eax,dword ptr ds:[esi]; // Get pointer to critter (?) + mov ebx,dword ptr ss:[esp+0x18]; // Get Bonus Ranged Damage + call DamageFunctionSub1; // Retrieve Raw Damage + add ebx,eax; // Raw Damage = Raw Damage + Bonus Ranged Damage + mov edx,dword ptr ss:[esp+0x28]; // Get armor DT + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + call GetAmmoDTMod; // Retrieve ammo DT (well, it's really Retrieve ammo DR, but since we're treating ammo DR as ammo DT...) + sub edx,eax; // DT = armor DT - ammo DT + test edx,edx; // Is DT >= 0? + jge bjmp; // If yes, skip the next instruction + xor edx,edx; // Otherwise, set DT = 0 +bjmp: + sub ebx,edx; // Raw Damage = Raw Damage - DT + test ebx,ebx; // Is Raw Damage <= 0? + jle cjmp; // If yes, skip damage calculation and go to bottom of loop + imul ebx,dword ptr ss:[esp+0x24]; // Otherwise, Raw Damage = Raw Damage * Damage Multiplier + test ebp,ebp; // Is Ammo Divisor == 0? + je djmp; // If yes, avoid divide by zero error + mov edx,ebx; + mov eax,ebx; + sar edx,0x1f; + idiv ebp; + mov ebx,eax; // Otherwise, Raw Damage = Raw Damage / Ammo Divisor +djmp: + mov edx,ebx; + mov eax,ebx; + sar edx,0x1f; + sub eax,edx; + sar eax,0x1; // Raw Damage = Raw Damage / 2 (related to critical hit damage multiplier bonus) + mov edx,dword ptr ss:[esp+0x20]; // Get combat difficulty setting (75 if wimpy, 100 if normal or if attacker is player, 125 if rough) + imul edx,eax; // Raw Damage = Raw Damage * combat difficulty setting + mov ebx,0x64; + mov eax,edx; + sar edx,0x1f; + idiv ebx; + mov ebx,eax; // Raw Damage = Raw Damage / 100 + mov edx,dword ptr ss:[esp+0x28]; // Get armor DT + mov eax,dword ptr ds:[esi+0x8]; // Get pointer to critter's weapon + call GetAmmoDTMod; // Retrieve ammo DT + sub edx,eax; // DT = armor DT - ammo DT + test edx,edx; // Is DT >= 0? + jge ejmp; // If yes, set DT = 0 + mov eax,0x0a; + imul eax,edx; // Otherwise, DT = DT * 10 (note that this should be a negative value) + jmp fjmp; +ejmp: + xor eax,eax; +fjmp: + mov edx,dword ptr ss:[esp+0x2c]; // Get armor DR + add edx,eax; // DR = armor DR + DT (note that DT should be less than or equal to zero) + test edx,edx; // Is DR >= 0? + jge gjmp; + xor edx,edx; // If no, set DR = 0 + jmp hjmp; +gjmp: + cmp edx,0x64; // Otherwise, is DR >= 100? + jge ijmp; // If yes, damage will be zero, so stop calculating and go to bottom of loop +hjmp: + imul edx,ebx; // Otherwise, Resisted Damage = DR * Raw Damage + mov dword ptr ss:[esp+0x30],0x64; + mov eax,edx; + sar edx,0x1f; + idiv dword ptr ss:[esp+0x30]; // Resisted Damage = Resisted Damage / 100 + sub ebx,eax; // Raw Damage = Raw Damage - Resisted Damage +cjmp: + test ebx,ebx; // Is Raw Damage <= 0? + jle ijmp; // If yes, don't accumulate damage + add dword ptr ds:[edi],ebx; // Otherwise, Accumulated Damage = Accumulated Damage + Raw Damage +ijmp: + mov eax,dword ptr ss:[esp+0x1c]; // Get number of hits + inc ecx; // counter += 1 + cmp ecx,eax; // Is counter < number of hits? + jl ajmp; // If yes, go back to start of damage calcuation loop (calculate damage for next hit) +end: + jmp DamageFunctionReturn; // Otherwise, exit loop + } +} + +static const DWORD stat_level = 0x4AEF48; +static const DWORD perk_level = 0x496B78; +static const DWORD MeleeDmgDispExit = 0x435C11; +static const DWORD MeleeDmgPrintExit = 0x439926; +static const DWORD MeleeExit = 0x47226D; +static const DWORD UnarmedExit = 0x472556; +static const DWORD FixaEnd = 0x4784B0; +static const DWORD FixbEnd = 0x478553; + +static __declspec(naked) void MeleeDmgDispFix() { + __asm { + call stat_level; // Get Melee Damage + mov ecx,eax; // Store value + mov eax,dword ptr ds:[0x6610b8]; // Get pointer to PC + mov edx,0x2; // perk_level argument: PERK_bonus_hth_damage + call perk_level; // Get rank of Bonus HtH Damage + shl eax,0x1; // Multiply by 2 + sub ecx,eax; // Subtract from Melee Damage + mov eax,ecx; // Move back to eax in preparation of push + jmp MeleeDmgDispExit; + } +} + +static __declspec(naked) void MeleeDmgPrintFix() { + __asm { + call stat_level; // Get Melee Damage + mov ebx,eax; // Store value + mov eax,dword ptr ds:[0x6610b8]; // Get pointer to PC + mov edx,0x2; // perk_level argument: PERK_bonus_hth_damage + call perk_level; // Get rank of Bonus HtH Damage + shl eax,0x1; // Multiply by 2 + sub ebx,eax; // Subtract from Melee Damage + mov eax,ebx; // Move back to eax in preparation of push + mov ebx,0x27a; // Facilitates printing to file + jmp MeleeDmgPrintExit; + } +} + +static __declspec(naked) void MeleeWeapDmgRngDispFix() { + __asm { + call stat_level; // Get Melee Damage + mov ebx,eax; // Store Melee Damage value + mov edx,dword ptr ds:[0x59e86c]; // Get pointer to critter + mov eax,dword ptr ds:[0x6610b8]; // Get pointer to PC + cmp eax,edx; // Is PC == critter? + jnz end; // exit if no + mov edx,0x2; // perk_level argument: PERK_bonus_hth_damage + call perk_level; // Otherwise, get rank of Bonus HtH Damage + shl eax,0x1; // Multiply by 2 + sub ebx,eax; // Subtract from Melee Damage +end: + jmp MeleeExit; + } +} + +static __declspec(naked) void UnarmedDmgRngDispFix() { + __asm { + mov ebx,eax; // Store Melee Damage value + mov edx,dword ptr ds:[0x59e86c]; // Get pointer to critter + mov eax,dword ptr ds:[0x6610b8]; // Get pointer to PC + cmp eax,edx; // Is PC == critter? + jnz end; // exit if no + mov edx,0x2; // perk_level argument: PERK_bonus_hth_damage + call perk_level; // Otherwise, get rank of Bonus HtH Damage + shl eax,0x1; // Multiply by 2 + sub ebx,eax; // Subtract to Melee Damage +end: + mov eax,ebx; // Move back to eax in prepartion of push + add eax,0x2; // Add 2 (max unarmed damage value) + push eax; // push to print string + mov eax,dword ptr ss:[esp+0x98]; + jmp UnarmedExit; + } +} + +static __declspec(naked) void HtHDamageFix1a() { + __asm { + mov eax,ecx; // stat_level argument: pointer to critter + mov edx,0xb; // stat_level argument: STAT_melee_dmg + call stat_level; // Get Total_Melee_Damage + add eax,2; // Total_Melee_Damage += 2 + mov dword ptr ss:[esp],eax; // Max_Damage = Total_Melee_Damage + xor edx,edx; + mov eax,dword ptr ds:[0x6610b8]; // Get pointer to PC + cmp ecx,eax; // Is the critter == PC? + jnz ajmp; // Skip to ajmp if no + mov edx,0x2; // perk_level argument: PERK_bonus_hth_damage + call perk_level; // Return Rank_of_Bonus_HtH_Damage_perk + shl eax,0x1; // Rank_of_Bonus_HtH_Damage_perk *= 2 + mov edx, eax; // Min_Damage = Rank_of_Bonus_HtH_Damage_perk +ajmp: + add edx,1; // Min_Damage += 1 + mov dword ptr ss:[esp+0x4],edx; // Store Min_Damage + sub esi,0x8; // Set up for switch statement beyond jump + jmp FixaEnd; + } +} + +static __declspec(naked) void HtHDamageFix1b() { + __asm { + call stat_level; // Get Total_Melee_Damage + mov ebp, eax; + mov eax, dword ptr ds:[0x6610b8]; // Get pointer to PC + cmp ecx, eax; // Is the critter == PC? + jnz end; // Skip to exit if no + mov edx, 0x2; // perk_level argument: PERK_bonus_hth_damage + call perk_level; // Return Rank_of_Bonus_HtH_Damage_perk + shl eax, 0x1; // Rank_of_Bonus_HtH_Damage_perk *= 2 + add dword ptr ss:[esp+0x4], eax; // Min_Damage += Rank_of_Bonus_HtH_Damage_perk +end: + jmp FixbEnd; + } +} + +static const DWORD roll_random=0x4A30C0; +static const DWORD UnarmedAttacksFixEnd=0x423A0D; +static void __declspec(naked) UnarmedAttacksFix() { + __asm { + cmp edx,0x10; // Power Kick + je PowKickHPunch; + cmp edx,0x9; // Hammer Punch + jnz HKickJabCheck; +PowKickHPunch: + mov edx,0x64; + mov eax,0x1; + call roll_random; + cmp eax,0x5; // 5% chance of critical hit + jle CriticalHit; + jmp end; +HKickJabCheck: + mov eax,dword ptr ds:[esi+0x4]; // get hit_mode + cmp eax,0x12; // Hook Kick + je HKickJab; + cmp eax,0xb; // Jab + jnz Haymaker; +HKickJab: + mov edx,0x64; + mov eax,0x1; + call roll_random; + cmp eax,0xa; // 10% chance of critical hit + jle CriticalHit; + jmp end; +Haymaker: + cmp dword ptr ds:[esi+0x4],0xa; // Haymaker + jnz PalmStrike; + mov edx,0x64; + mov eax,0x1; + call roll_random; + cmp eax,0xf; // 15% chance of critical hit + jle CriticalHit; + jmp end; +PalmStrike: + cmp dword ptr ds:[esi+0x4],0xc; // Palm Strike + jnz PiercingStrike; + mov edx,0x64; + mov eax,0x1; + call roll_random; + cmp eax,0x14; // 20% chance of critical hit + jle CriticalHit; + jmp end; +PiercingStrike: + cmp dword ptr ds:[esi+0x4],0xd; // Piercing Strike + jnz PiercingKick; + mov edx,0x64; + mov eax,0x1; + call roll_random; + cmp eax,0x28; // 40% chance of critical hit + jle CriticalHit; + jmp end; +PiercingKick: + cmp dword ptr ds:[esi+0x4],0x13; // Piercing Kick + jnz end; + mov edx,0x64; + mov eax,0x1; + call roll_random; + cmp eax,0x32; // 50% chance of critical hit + jg end; +CriticalHit: + mov ebx,0x3; // Upgrade to critical hit +end: + jmp UnarmedAttacksFixEnd; + } +} + +void AmmoModInit() { + int formula; + if(formula=GetPrivateProfileInt("Misc", "DamageFormula", 0, ini)) { + switch(formula) { + case 1: + MakeCall(0x424995, &DamageFunction1, true); + break; + case 2: + MakeCall(0x424995, &DamageFunction2, true); + break; + /*case 3: + MakeCall(0x424995, &DamageFunction3, true); + break;*/ + case 4: + MakeCall(0x424995, &DamageFunction4, true); + break; + case 5: + MakeCall(0x424995, &DamageFunction5, true); + } + } + if(GetPrivateProfileInt("Misc", "BonusHtHDamageFix", 1, ini)) { + dlog("Applying Bonus HtH Damage Perk fix.", DL_INIT); + MakeCall(0x435C0C, &MeleeDmgDispFix, true); + MakeCall(0x43991C, &MeleeDmgPrintFix, true); + MakeCall(0x472266, &MeleeWeapDmgRngDispFix, true); + MakeCall(0x47254B, &UnarmedDmgRngDispFix, true); + MakeCall(0x478492, &HtHDamageFix1a, true); + MakeCall(0x47854C, &HtHDamageFix1b, true); + dlogr(" Done", DL_INIT); + } + if(GetPrivateProfileIntA("Misc", "SpecialUnarmedAttacksFix", 1, ini)) { + dlog("Applying Special Unarmed Attacks fix.", DL_INIT); + MakeCall(0x42394D, &UnarmedAttacksFix, true); + dlogr(" Done", DL_INIT); + } +} \ No newline at end of file diff --git a/sfall/AmmoMod.h b/sfall/AmmoMod.h new file mode 100644 index 00000000..cc17943e --- /dev/null +++ b/sfall/AmmoMod.h @@ -0,0 +1,21 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void AmmoModInit(); \ No newline at end of file diff --git a/sfall/AnimationsAtOnceLimit.cpp b/sfall/AnimationsAtOnceLimit.cpp new file mode 100644 index 00000000..1fa4cd6f --- /dev/null +++ b/sfall/AnimationsAtOnceLimit.cpp @@ -0,0 +1,378 @@ +/* + * sfall + * Copyright (C) 2009, 2010 Mash (Matt Wells, mashw at bigpond dot net dot au) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "AnimationsAtOnceLimit.h" + + +static bool AniLimitFixActive=false; + +//pointers to new animation struct arrays +static BYTE *aniMem; +static BYTE *aniMem2; + +void AnimationsAtOnceInit(signed char aniMax) { + + if(aniMax<=32) return; + + AniLimitFixActive=true; + + //allocate memory to store larger animation struct arrays + aniMem = new BYTE[2656*(aniMax+1)]; + aniMem2 = new BYTE[3240*(aniMax+1)]; + + + //set general animation limit check (old 20) aniMax-12 -- +12 reserved for PC movement(4) + other critical animations(8)? + SafeWrite8(0x413C07, aniMax-12); + + //PC movement animation limit checks (old 24) aniMax-8 -- +8 reserved for other critical animations?. + SafeWrite8(0x416E11, aniMax-8); + SafeWrite8(0x416F64, aniMax-8); + SafeWrite8(0x417143, aniMax-8); + SafeWrite8(0x41725C, aniMax-8); + SafeWrite8(0x4179CC, aniMax-8); + + //Max animation limit checks (old 32) aniMax + SafeWrite8(0x413A70, aniMax); + SafeWrite8(0x413ADD, aniMax); + SafeWrite8(0x413BDD, aniMax); + SafeWrite8(0x413EB8, aniMax); + SafeWrite8(0x413F4E, aniMax); + SafeWrite8(0x4186F1, aniMax); + + + //Max animations checks - animation struct size * max num of animations (old 2656*32=84992) + SafeWrite32(0x413AA9, 2656*aniMax); + SafeWrite32(0x413CB7, 2656*aniMax); + SafeWrite32(0x413DC2, 2656*aniMax); + SafeWrite32(0x417F3A, 2656*aniMax); + + + //divert old animation structure list pointers to newly alocated memory + + //struct array 1/////////////////// + + //old addr 0x54C1B4 + SafeWrite32(0x413A9E, (DWORD)aniMem); + + //old addr 0x54C1C0 + SafeWrite32(0x413AA4, 12+(DWORD)aniMem); + SafeWrite32(0x413DBC, 12+(DWORD)aniMem); + + //old addr 0x54CC14 + SafeWrite32(0x413B96, 2656+(DWORD)aniMem); + SafeWrite32(0x413C5A, 2656+(DWORD)aniMem); + SafeWrite32(0x413CF0, 2656+(DWORD)aniMem); + SafeWrite32(0x413DE1, 2656+(DWORD)aniMem); + SafeWrite32(0x413E66, 2656+(DWORD)aniMem); + SafeWrite32(0x413EF3, 2656+(DWORD)aniMem); + SafeWrite32(0x413FA2, 2656+(DWORD)aniMem); + SafeWrite32(0x414161, 2656+(DWORD)aniMem); + SafeWrite32(0x4142D3, 2656+(DWORD)aniMem); + SafeWrite32(0x41449A, 2656+(DWORD)aniMem); + SafeWrite32(0x41460B, 2656+(DWORD)aniMem); + SafeWrite32(0x4146FF, 2656+(DWORD)aniMem); + SafeWrite32(0x414826, 2656+(DWORD)aniMem); + SafeWrite32(0x41491A, 2656+(DWORD)aniMem); + SafeWrite32(0x4149F8, 2656+(DWORD)aniMem); + SafeWrite32(0x414AD0, 2656+(DWORD)aniMem); + SafeWrite32(0x414BA4, 2656+(DWORD)aniMem); + SafeWrite32(0x414C8C, 2656+(DWORD)aniMem); + SafeWrite32(0x414CF0, 2656+(DWORD)aniMem); + SafeWrite32(0x414D60, 2656+(DWORD)aniMem); + SafeWrite32(0x414DD0, 2656+(DWORD)aniMem); + SafeWrite32(0x414E48, 2656+(DWORD)aniMem); + SafeWrite32(0x414EDA, 2656+(DWORD)aniMem); + SafeWrite32(0x414F5E, 2656+(DWORD)aniMem); + SafeWrite32(0x414FEE, 2656+(DWORD)aniMem); + SafeWrite32(0x41505C, 2656+(DWORD)aniMem); + SafeWrite32(0x4150D0, 2656+(DWORD)aniMem); + SafeWrite32(0x415158, 2656+(DWORD)aniMem); + SafeWrite32(0x4151B8, 2656+(DWORD)aniMem); + SafeWrite32(0x415286, 2656+(DWORD)aniMem); + SafeWrite32(0x41535C, 2656+(DWORD)aniMem); + SafeWrite32(0x4153D0, 2656+(DWORD)aniMem); + SafeWrite32(0x41544A, 2656+(DWORD)aniMem); + SafeWrite32(0x4154EC, 2656+(DWORD)aniMem); + SafeWrite32(0x4155EA, 2656+(DWORD)aniMem); + SafeWrite32(0x4156C0, 2656+(DWORD)aniMem); + SafeWrite32(0x4156D5, 2656+(DWORD)aniMem); + SafeWrite32(0x4156F2, 2656+(DWORD)aniMem); + SafeWrite32(0x41572F, 2656+(DWORD)aniMem); + SafeWrite32(0x41573E, 2656+(DWORD)aniMem); + SafeWrite32(0x415B1B, 2656+(DWORD)aniMem); + SafeWrite32(0x415B56, 2656+(DWORD)aniMem); + SafeWrite32(0x415BB6, 2656+(DWORD)aniMem); + SafeWrite32(0x415C7C, 2656+(DWORD)aniMem); + SafeWrite32(0x415CA3, 2656+(DWORD)aniMem); + SafeWrite32(0x415DE4, 2656+(DWORD)aniMem); + + //old addr 0x54CC18 + SafeWrite32(0x413D07, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415700, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415B6B, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415B78, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415C2D, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415D38, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415D56, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415D63, 2656+4+(DWORD)aniMem); + SafeWrite32(0x415DCF, 2656+4+(DWORD)aniMem); + + //old addr 0x54CC1C + SafeWrite32(0x413C6A, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413CA3, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413CF6, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413E76, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413EA4, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413F03, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413F20, 2656+8+(DWORD)aniMem); + SafeWrite32(0x413F3A, 2656+8+(DWORD)aniMem); + SafeWrite32(0x4156EC, 2656+8+(DWORD)aniMem); + SafeWrite32(0x415B72, 2656+8+(DWORD)aniMem); + SafeWrite32(0x415C18, 2656+8+(DWORD)aniMem); + SafeWrite32(0x415C58, 2656+8+(DWORD)aniMem); + SafeWrite32(0x415C6D, 2656+8+(DWORD)aniMem); + SafeWrite32(0x415DBE, 2656+8+(DWORD)aniMem); + + //old addr 0x54CC20 + SafeWrite32(0x413B2A, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413B33, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413B43, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413B54, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413B66, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413BA2, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413BAB, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413BC0, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413BCD, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413C3C, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413C87, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413D01, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413D10, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413D36, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413D53, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413DAD, 2656+12+(DWORD)aniMem); + SafeWrite32(0x413E93, 2656+12+(DWORD)aniMem); + SafeWrite32(0x4155DF, 2656+12+(DWORD)aniMem); + SafeWrite32(0x415AE2, 2656+12+(DWORD)aniMem); + SafeWrite32(0x415D9A, 2656+12+(DWORD)aniMem); + SafeWrite32(0x415DDE, 2656+12+(DWORD)aniMem); + SafeWrite32(0x415E06, 2656+12+(DWORD)aniMem); + SafeWrite32(0x415E12, 2656+12+(DWORD)aniMem); + SafeWrite32(0x417F25, 2656+12+(DWORD)aniMem); + SafeWrite32(0x417F30, 2656+12+(DWORD)aniMem); + + //old addr 0x54CC24 + SafeWrite32(0x413C7E, 2656+16+(DWORD)aniMem); + SafeWrite32(0x413E8A, 2656+16+(DWORD)aniMem); + SafeWrite32(0x413F17, 2656+16+(DWORD)aniMem); + SafeWrite32(0x415C24, 2656+16+(DWORD)aniMem); + SafeWrite32(0x415D16, 2656+16+(DWORD)aniMem); + SafeWrite32(0x415D44, 2656+16+(DWORD)aniMem); + + //old addr 0x54CC28 + SafeWrite32(0x413C76, 2656+20+(DWORD)aniMem); + SafeWrite32(0x413E82, 2656+20+(DWORD)aniMem); + SafeWrite32(0x413F0F, 2656+20+(DWORD)aniMem); + SafeWrite32(0x415C3E, 2656+20+(DWORD)aniMem); + SafeWrite32(0x415D0E, 2656+20+(DWORD)aniMem); + SafeWrite32(0x415D4D, 2656+20+(DWORD)aniMem); + + //old addr 0x54CC38 + SafeWrite32(0x413F29, 2656+36+(DWORD)aniMem); + + //old addr 0x54CC3C + SafeWrite32(0x413D1C, 2656+40+(DWORD)aniMem); + SafeWrite32(0x41570D, 2656+40+(DWORD)aniMem); + SafeWrite32(0x415720, 2656+40+(DWORD)aniMem); + + //old addr 0x54CC48 + SafeWrite32(0x415C35, 2656+52+(DWORD)aniMem); + + + //struct array 2/////////////////// + + //old addr 0x530014 + SafeWrite32(0x416E4A, (DWORD)aniMem2); + SafeWrite32(0x416E56, (DWORD)aniMem2); + SafeWrite32(0x416EBD, (DWORD)aniMem2); + SafeWrite32(0x416F98, (DWORD)aniMem2); + SafeWrite32(0x416FAC, (DWORD)aniMem2); + SafeWrite32(0x4170C5, (DWORD)aniMem2); + SafeWrite32(0x417167, (DWORD)aniMem2); + SafeWrite32(0x4171F6, (DWORD)aniMem2); + SafeWrite32(0x4172A5, (DWORD)aniMem2); + SafeWrite32(0x417583, (DWORD)aniMem2); + SafeWrite32(0x417856, (DWORD)aniMem2); + SafeWrite32(0x4178AE, (DWORD)aniMem2); + SafeWrite32(0x417937, (DWORD)aniMem2); + SafeWrite32(0x4179FA, (DWORD)aniMem2); + SafeWrite32(0x417A86, (DWORD)aniMem2); + SafeWrite32(0x417BB7, (DWORD)aniMem2); + SafeWrite32(0x417CD1, (DWORD)aniMem2); + SafeWrite32(0x417D54, (DWORD)aniMem2); + SafeWrite32(0x417E14, (DWORD)aniMem2); + SafeWrite32(0x417E3C, (DWORD)aniMem2); + SafeWrite32(0x417FB1, (DWORD)aniMem2); + SafeWrite32(0x417FB7, (DWORD)aniMem2); + SafeWrite32(0x417FCC, (DWORD)aniMem2); + + //old addr 0x530018 + SafeWrite32(0x415D7D, 4+(DWORD)aniMem2); + SafeWrite32(0x416E40, 4+(DWORD)aniMem2); + SafeWrite32(0x416F8F, 4+(DWORD)aniMem2); + SafeWrite32(0x41738B, 4+(DWORD)aniMem2); + SafeWrite32(0x417786, 4+(DWORD)aniMem2); + SafeWrite32(0x4177E7, 4+(DWORD)aniMem2); + SafeWrite32(0x417983, 4+(DWORD)aniMem2); + SafeWrite32(0x417AC1, 4+(DWORD)aniMem2); + SafeWrite32(0x417B70, 4+(DWORD)aniMem2); + SafeWrite32(0x417C0D, 4+(DWORD)aniMem2); + + //old addr 0x53001C + SafeWrite32(0x416EB8, 8+(DWORD)aniMem2); + SafeWrite32(0x416ECB, 8+(DWORD)aniMem2); + SafeWrite32(0x416FA6, 8+(DWORD)aniMem2); + SafeWrite32(0x416FFF, 8+(DWORD)aniMem2); + SafeWrite32(0x41702F, 8+(DWORD)aniMem2); + SafeWrite32(0x4177F9, 8+(DWORD)aniMem2); + SafeWrite32(0x417AC7, 8+(DWORD)aniMem2); + SafeWrite32(0x417ADB, 8+(DWORD)aniMem2); + SafeWrite32(0x417C63, 8+(DWORD)aniMem2); + SafeWrite32(0x417CA3, 8+(DWORD)aniMem2); + + //old addr 0x530020 + SafeWrite32(0x416EF8, 12+(DWORD)aniMem2); + SafeWrite32(0x4173EE, 12+(DWORD)aniMem2); + + //old addr 0x530024 + SafeWrite32(0x416EC3, 16+(DWORD)aniMem2); + SafeWrite32(0x417035, 16+(DWORD)aniMem2); + SafeWrite32(0x417AD5, 16+(DWORD)aniMem2); + SafeWrite32(0x417B7B, 16+(DWORD)aniMem2); + SafeWrite32(0x417B9A, 16+(DWORD)aniMem2); + + //old addr 0x530028 + SafeWrite32(0x416ED8, 20+(DWORD)aniMem2); + SafeWrite32(0x417066, 20+(DWORD)aniMem2); + SafeWrite32(0x417B08, 20+(DWORD)aniMem2); + SafeWrite32(0x417B88, 20+(DWORD)aniMem2); + + //old addr 0x53002C + SafeWrite32(0x415BF7, 24+(DWORD)aniMem2); + SafeWrite32(0x416EEC, 24+(DWORD)aniMem2); + SafeWrite32(0x41706C, 24+(DWORD)aniMem2); + SafeWrite32(0x4177AB, 24+(DWORD)aniMem2); + SafeWrite32(0x4179A4, 24+(DWORD)aniMem2); + SafeWrite32(0x417ACF, 24+(DWORD)aniMem2); + SafeWrite32(0x417B94, 24+(DWORD)aniMem2); + SafeWrite32(0x417C30, 24+(DWORD)aniMem2); + SafeWrite32(0x417D73, 24+(DWORD)aniMem2); + SafeWrite32(0x417E78, 24+(DWORD)aniMem2); + + //old addr 0x530030 + SafeWrite32(0x416869, 28+(DWORD)aniMem2); + SafeWrite32(0x416871, 28+(DWORD)aniMem2); + SafeWrite32(0x4168B0, 28+(DWORD)aniMem2); + SafeWrite32(0x4168FC, 28+(DWORD)aniMem2); + SafeWrite32(0x416942, 28+(DWORD)aniMem2); + SafeWrite32(0x41694A, 28+(DWORD)aniMem2); + SafeWrite32(0x416D6D, 28+(DWORD)aniMem2); + SafeWrite32(0x416D74, 28+(DWORD)aniMem2); + SafeWrite32(0x416DB2, 28+(DWORD)aniMem2); + SafeWrite32(0x416DE4, 28+(DWORD)aniMem2); + SafeWrite32(0x416DEC, 28+(DWORD)aniMem2); + SafeWrite32(0x416F08, 28+(DWORD)aniMem2); + SafeWrite32(0x416F36, 28+(DWORD)aniMem2); + SafeWrite32(0x4170FC, 28+(DWORD)aniMem2); + SafeWrite32(0x41759D, 28+(DWORD)aniMem2); + SafeWrite32(0x4176EE, 28+(DWORD)aniMem2); + SafeWrite32(0x4178A7, 28+(DWORD)aniMem2); + SafeWrite32(0x41792F, 28+(DWORD)aniMem2); + SafeWrite32(0x417B1A, 28+(DWORD)aniMem2); + SafeWrite32(0x417BAE, 28+(DWORD)aniMem2); + + //old addr 0x530034 + SafeWrite32(0x415BFF, 32+(DWORD)aniMem2); + SafeWrite32(0x415D85, 32+(DWORD)aniMem2); + SafeWrite32(0x41687B, 32+(DWORD)aniMem2); + SafeWrite32(0x416D7E, 32+(DWORD)aniMem2); + SafeWrite32(0x416E7A, 32+(DWORD)aniMem2); + SafeWrite32(0x416F12, 32+(DWORD)aniMem2); + SafeWrite32(0x417023, 32+(DWORD)aniMem2); + SafeWrite32(0x417106, 32+(DWORD)aniMem2); + SafeWrite32(0x417385, 32+(DWORD)aniMem2); + SafeWrite32(0x417434, 32+(DWORD)aniMem2); + SafeWrite32(0x4174BA, 32+(DWORD)aniMem2); + SafeWrite32(0x4175A7, 32+(DWORD)aniMem2); + SafeWrite32(0x41760D, 32+(DWORD)aniMem2); + SafeWrite32(0x4176E7, 32+(DWORD)aniMem2); + SafeWrite32(0x4176F4, 32+(DWORD)aniMem2); + SafeWrite32(0x41771E, 32+(DWORD)aniMem2); + SafeWrite32(0x41779A, 32+(DWORD)aniMem2); + SafeWrite32(0x4177E1, 32+(DWORD)aniMem2); + SafeWrite32(0x417806, 32+(DWORD)aniMem2); + SafeWrite32(0x4178A1, 32+(DWORD)aniMem2); + SafeWrite32(0x4178B4, 32+(DWORD)aniMem2); + SafeWrite32(0x41790B, 32+(DWORD)aniMem2); + SafeWrite32(0x417929, 32+(DWORD)aniMem2); + SafeWrite32(0x417961, 32+(DWORD)aniMem2); + SafeWrite32(0x417993, 32+(DWORD)aniMem2); + SafeWrite32(0x417B0E, 32+(DWORD)aniMem2); + SafeWrite32(0x417B60, 32+(DWORD)aniMem2); + SafeWrite32(0x417BF8, 32+(DWORD)aniMem2); + SafeWrite32(0x417C15, 32+(DWORD)aniMem2); + SafeWrite32(0x417C21, 32+(DWORD)aniMem2); + SafeWrite32(0x417C4B, 32+(DWORD)aniMem2); + SafeWrite32(0x417D79, 32+(DWORD)aniMem2); + SafeWrite32(0x417E31, 32+(DWORD)aniMem2); + SafeWrite32(0x417F58, 32+(DWORD)aniMem2); + SafeWrite32(0x417F81, 32+(DWORD)aniMem2); + SafeWrite32(0x417FC6, 32+(DWORD)aniMem2); + + //old addr 0x530038 + SafeWrite32(0x4168D7, 36+(DWORD)aniMem2); + SafeWrite32(0x416914, 36+(DWORD)aniMem2); + SafeWrite32(0x41691F, 36+(DWORD)aniMem2); + SafeWrite32(0x416DD9, 36+(DWORD)aniMem2); + SafeWrite32(0x416EE1, 36+(DWORD)aniMem2); + SafeWrite32(0x41758F, 36+(DWORD)aniMem2); + + //old addr 0x53003A + SafeWrite32(0x416903, 38+(DWORD)aniMem2); + + //old addr 0x53003B + SafeWrite32(0x4168C5, 39+(DWORD)aniMem2); + SafeWrite32(0x416DBD, 39+(DWORD)aniMem2); + + //old addr 0x53003C + SafeWrite32(0x4173CE, 40+(DWORD)aniMem2); + SafeWrite32(0x4174C1, 40+(DWORD)aniMem2); + SafeWrite32(0x4175F1, 40+(DWORD)aniMem2); + SafeWrite32(0x417730, 40+(DWORD)aniMem2); + +} + + + +void AnimationsAtOnceExit() { + if(!AniLimitFixActive) return; + delete[] aniMem; + delete[] aniMem2; +} + diff --git a/sfall/AnimationsAtOnceLimit.h b/sfall/AnimationsAtOnceLimit.h new file mode 100644 index 00000000..1d117d15 --- /dev/null +++ b/sfall/AnimationsAtOnceLimit.h @@ -0,0 +1,26 @@ +/* + * sfall + * Copyright (C) 2009 Mash (Matt Wells, mashw at bigpond dot net dot au) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#pragma once + + +void AnimationsAtOnceInit(signed char aniMax); +void AnimationsAtOnceExit(); + + diff --git a/sfall/Arrays.cpp b/sfall/Arrays.cpp new file mode 100644 index 00000000..325a8c97 --- /dev/null +++ b/sfall/Arrays.cpp @@ -0,0 +1,605 @@ +#include +#include + +#include "Arrays.h" +#include "ScriptExtender.h" + +/* + GLOBAL variable for arrays +*/ +DWORD arraysBehavior = 1; // 0 - backward compatible with pre-3.4, 1 - permanent arrays don't get stored in savegames by default +// arrays map: arrayId => arrayVar +ArraysMap arrays; +// auto-incremented ID +DWORD nextarrayid=1; +// temp arrays: set of arrayId +std::set tempArrays; +// saved arrays: arrayKey => arrayId +ArrayKeysMap savedArrays; + +// special array ID for array expressions +DWORD stackArrayId; + +static char get_all_arrays_special_key[] = "...all_arrays..."; + +sArrayElement::sArrayElement() : len(0), intVal(0), type(DATATYPE_NONE) +{ + +} + +sArrayElement::sArrayElement( DWORD _val, DWORD _dataType ) : len(0), type(_dataType), intVal(_val) +{ + if (DATATYPE_STR == _dataType) + len = strlen((char*)_val); +} + +sArrayElement::sArrayElement(const long& other) +{ + set(other); +} + +void sArrayElement::clear() +{ + if (type == DATATYPE_STR && strVal) + delete[] strVal; +} + +void sArrayElement::setByType( DWORD val, DWORD dataType ) +{ + switch(dataType) { + case DATATYPE_STR: + set((char*)val); + break; + case DATATYPE_INT: + set(*(long*)&val); + break; + case DATATYPE_FLOAT: + set(*(float*)&val); + break; + } +} + +void sArrayElement::set( long val ) +{ + clear(); + type = DATATYPE_INT; + intVal = val; +} + +void sArrayElement::set( float val ) +{ + clear(); + type = DATATYPE_FLOAT; + floatVal = val; +} + +void sArrayElement::set( const char* val, int _len /*= -1*/ ) +{ + clear(); + type = DATATYPE_STR; + if (_len == -1) + _len = strlen(val); + if (_len > ARRAY_MAX_STRING-1) // memory safety + _len = ARRAY_MAX_STRING-1; + len = _len + 1; + strVal = new char[len]; + memcpy(strVal, val, _len); + strVal[_len] = '\0'; +} + +void sArrayElement::unset() +{ + clear(); + type = DATATYPE_NONE; + len = intVal = 0; +} + +DWORD sArrayElement::getHashStatic(DWORD value, DWORD type) { + switch (type) { + case DATATYPE_STR: + const char* str; + str = (const char*)value; + int i; + DWORD res; + for (i=0, res=0; str[i]!='\0'; i++) { + res = ((res << 5) + res) + str[i]; + } + return res; + case DATATYPE_INT: + case DATATYPE_FLOAT: + return value; + default: + return 0; + } +} + +bool sArrayElement::operator<( const sArrayElement &el ) const +{ + if (type < el.type) { + return true; + } else if (type == el.type) { + switch (type) { + case DATATYPE_STR: + return strcmp(strVal, el.strVal) < 0; + case DATATYPE_FLOAT: + return floatVal < el.floatVal; + case DATATYPE_INT: + default: + return intVal < el.intVal; + } + } else { + return false; + } +} + +bool sArrayElement_EqualFunc::operator()( const sArrayElement &elA, const sArrayElement &elB ) const +{ + if (elA.type != elB.type) + return false; + switch (elA.type) { + case DATATYPE_STR: + return strcmp(elA.strVal, elB.strVal) == 0; + case DATATYPE_INT: + case DATATYPE_FLOAT: + return elA.intVal == elB.intVal; + default: + return true; + } +} + +void sArrayVar::clearRange( int from, int to /*= -1*/ ) +{ + if (to == -1) + to = val.size(); + std::vector::iterator it, itTo = val.begin() + to; + for (it = val.begin() + from; it < itTo; ++it) { + it->clear(); + } +} + +void SaveArrayElement(sArrayElement* el, HANDLE h) +{ + DWORD unused; + WriteFile(h, &el->type, 4, &unused, 0); + if (el->type == DATATYPE_STR) { + WriteFile(h, &el->len, 4, &unused, 0); + WriteFile(h, el->strVal, el->len, &unused, 0); + } else { + WriteFile(h, &el->intVal, 4, &unused, 0); + } +} + +void LoadArrayElement(sArrayElement* el, HANDLE h) +{ + DWORD unused; + ReadFile(h, &el->type, 4, &unused, 0); + if (el->type == DATATYPE_STR) { + ReadFile(h, &el->len, 4, &unused, 0); + if (el->len > 0) { + el->strVal = new char[el->len]; + ReadFile(h, el->strVal, el->len, &unused, 0); + } else + el->strVal = NULL; + } else { + ReadFile(h, &el->intVal, 4, &unused, 0); + } +} + +void LoadArraysOld(HANDLE h) { + dlogr("Loading arrays (old fmt)", DL_MAIN); + DWORD count, unused, id, j; + ReadFile(h, &count, 4, &unused, 0); + if(unused!=4) return; + sArrayVarOld var; + sArrayVar varN; + for(DWORD i=0;i 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4 + arrayVar.key.intVal = arrayVar.key.type; + arrayVar.key.type = DATATYPE_INT; + } + ReadFile(h, &arrayVar.flags, 4, &unused, 0); + ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values + bool isAssoc = arrayVar.isAssoc(); + arrayVar.val.resize(elCount); + for (j=0; jsecond); + if (arIt == arrays.end()) { + savedArrays.erase(it++); + } else { + ++count; + ++it; + } + } + WriteFile(h, &count, 4, &unused, 0); + for (it = savedArrays.begin(); it != savedArrays.end(); ++it) { + arIt = arrays.find(it->second); + if (arIt != arrays.end()) { + sArrayVar &var = arrays[it->second]; + SaveArrayElement(&var.key, h); + WriteFile(h, &var.flags, 4, &unused, 0); + elCount = var.val.size(); + WriteFile(h, &elCount, 4, &unused, 0); + for (std::vector::iterator it = var.val.begin(); it != var.val.end(); ++it) { + SaveArrayElement(&(*it), h); + } + } + } +} + +int GetNumArrays() { + return arrays.size(); +} +void GetArrays(int* _arrays) { + int pos=0; + array_citr itr=arrays.begin(); + while(itr!=arrays.end()) { + _arrays[pos++]=itr->first; + _arrays[pos++]=itr->second.size(); + _arrays[pos++]=itr->second.flags; + itr++; + } +} +// those too are not really used yet in FalloutClient (AFAIK) -- phobos2077 +void DEGetArray(int id, DWORD* types, void* data) { + //memcpy(types, arrays[id].types, arrays[id].len*4); + //memcpy(data, arrays[id].data, arrays[id].len*arrays[id].datalen); +} +void DESetArray(int id, const DWORD* types, const void* data) { + //if(types) memcpy(arrays[id].types, types, arrays[id].len*4); + //memcpy(arrays[id].data, data, arrays[id].len*arrays[id].datalen); +} + + +/* + Array manipulation functions for script operators +*/ +DWORD _stdcall getSfallTypeByScriptType(DWORD varType) { + varType&=0xffff; + switch (varType) { + case VAR_TYPE_STR: + case VAR_TYPE_STR2: + return DATATYPE_STR; + case VAR_TYPE_FLOAT: + return DATATYPE_FLOAT; + case VAR_TYPE_INT: + default: + return DATATYPE_INT; + } +} + +DWORD _stdcall getScriptTypeBySfallType(DWORD dataType) { + switch (dataType) { + case DATATYPE_STR: + return VAR_TYPE_STR; + case DATATYPE_FLOAT: + return VAR_TYPE_FLOAT; + case DATATYPE_INT: + default: + return VAR_TYPE_INT; + } +} + +DWORD _stdcall CreateArray(int len, DWORD nothing) { + sArrayVar var; + if (len < 0) var.flags |= ARRAYFLAG_ASSOC; + else if (len > ARRAY_MAX_SIZE) len = ARRAY_MAX_SIZE; // safecheck + if (!var.isAssoc()) { + var.val.resize(len); + } + while(arrays.find(nextarrayid)!=arrays.end()) nextarrayid++; + if (nextarrayid == 0) nextarrayid++; + if (arraysBehavior == 0) { + var.key = sArrayElement(nextarrayid, DATATYPE_INT); + savedArrays[var.key] = nextarrayid; + } + stackArrayId = nextarrayid; + arrays[nextarrayid]=var; + return nextarrayid++; +} +DWORD _stdcall TempArray(DWORD len, DWORD nothing) { + DWORD id=CreateArray(len, nothing); + tempArrays.insert(id); + return id; +} +void _stdcall FreeArray(DWORD id) { + array_itr it = arrays.find(id); + if (it != arrays.end()) { + savedArrays.erase(it->second.key); + it->second.clear(); + arrays.erase(id); + } +} + +DWORD _stdcall GetArrayKey(DWORD id, int index, DWORD* resultType) { + *resultType = VAR_TYPE_INT; + if (arrays.find(id) == arrays.end() || index < -1 || index > arrays[id].size()) return 0; + if (index == -1) // special index to indicate if array is associative + return (DWORD)arrays[id].isAssoc(); + if (arrays[id].isAssoc()) { + index *= 2; + // for associative array - return key at the specified index + switch(arrays[id].val[index].type) { + case DATATYPE_INT: + return *(DWORD *)&arrays[id].val[index].intVal; + case DATATYPE_FLOAT: + *resultType = VAR_TYPE_FLOAT; + return *(DWORD *)&arrays[id].val[index].intVal; + case DATATYPE_STR: + *resultType = VAR_TYPE_STR; + return (DWORD)arrays[id].val[index].strVal; + case DATATYPE_NONE: + default: + return 0; + } + } else { + // just return index for normal array.. + return index; + } +} + +DWORD _stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType) { + *resultType = VAR_TYPE_INT; + if(arrays.find(id)==arrays.end()) return 0; + int el; + sArrayVar &arr = arrays[id]; + if (arr.isAssoc()) { + ArrayKeysMap::iterator it = arr.keyHash.find(sArrayElement(key, getSfallTypeByScriptType(keyType))); + if (it != arr.keyHash.end()) + el = it->second + 1; + else + return 0; + } else { + el = key; + if (arr.size() <= el) return 0; + } + switch(arr.val[el].type) { + case DATATYPE_NONE: return 0; + case DATATYPE_INT: + return *(DWORD *)&arr.val[el].intVal; + case DATATYPE_FLOAT: + *resultType = VAR_TYPE_FLOAT; + return *(DWORD *)&arr.val[el].intVal; + case DATATYPE_STR: + *resultType = VAR_TYPE_STR; + return (DWORD)arr.val[el].strVal; + } + return 0; +} +void _stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset) { + keyType = getSfallTypeByScriptType(keyType); + valType = getSfallTypeByScriptType(valType); + if(arrays.find(id)==arrays.end()) return; + int el; + sArrayVar &arr = arrays[id]; + if (arrays[id].isAssoc()) { + sArrayElement sEl(key, keyType); + ArrayKeysMap::iterator elIter = arr.keyHash.find(sEl); + el = (elIter != arr.keyHash.end()) ? elIter->second : -1; + if (valType == DATATYPE_INT && val == 0 && allowUnset) { + // after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset + if (el >= 0) { + // remove key=>value pair from vector + arr.clearRange(el, el + 2); + arr.val.erase(arr.val.begin() + el, arr.val.begin() + (el + 2)); + // remove from hashtable + arr.keyHash.erase(elIter); + // shift all keyHash references + for (elIter = arr.keyHash.begin(); elIter != arr.keyHash.end(); ++elIter) { + if (elIter->second >= (DWORD)el) + elIter->second -= 2; + } + } + } else { + if (el == -1) { + // size check + if (arr.size() >= ARRAY_MAX_SIZE) return; + // add pair + el = arr.val.size(); + arr.val.resize(el + 2); + arr.val[el].setByType(key, keyType); // copy data + arr.keyHash[arr.val[el]] = el; + } + arr.val[el+1].setByType(val, valType); + } + } else { + // only update normal array if key is an integer and within array size + el = key; + if (keyType == DATATYPE_INT && arr.size() > el) { + arr.val[el].setByType(val, valType); + } + } +} +int _stdcall LenArray(DWORD id) { + if(arrays.find(id)==arrays.end()) return -1; + else return arrays[id].size(); +} +void _stdcall ResizeArray(DWORD id, int newlen) { + if (arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return; + sArrayVar &arr = arrays[id]; + if (arr.isAssoc()) { + // only allow to reduce number of elements (adding range of elements is meaningless for maps) + if (newlen < arrays[id].size()) { + ArrayKeysMap::iterator itHash; + std::vector::iterator itVal; + int actualLen = newlen*2; + for (itVal = arr.val.begin() + actualLen; itVal != arr.val.end(); itVal += 2) { + if ((itHash = arr.keyHash.find(*itVal)) != arr.keyHash.end()) + arr.keyHash.erase(itHash); + } + arr.clearRange(actualLen); + arr.val.resize(actualLen); + } + return; + } + if (newlen >= 0) { // actual resize + if (newlen > ARRAY_MAX_SIZE) // safety + newlen = ARRAY_MAX_SIZE; + if (newlen < arr.size()) + arr.clearRange(newlen); + arr.val.resize(newlen); + } else { // special functions for lists... + switch (newlen) { + case ARRAY_ACTION_SORT: // sort ascending + std::sort(arr.val.begin(), arr.val.end()); + break; + case ARRAY_ACTION_RSORT: // sort descending + std::sort(arr.val.rbegin(), arr.val.rend()); + break; + case ARRAY_ACTION_REVERSE: // reverse elements + std::reverse(arr.val.rbegin(), arr.val.rend()); + break; + case ARRAY_ACTION_SHUFFLE: // shuffle elements + std::random_shuffle(arr.val.rbegin(), arr.val.rend()); + break; + } + } +} +void _stdcall FixArray(DWORD id) { + tempArrays.erase(id); +} +int _stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType) { + *resultType = VAR_TYPE_INT; + datatype = getSfallTypeByScriptType(datatype); + if (arrays.find(id) == arrays.end()) return -1; + char step = arrays[id].isAssoc() ? 2 : 1; + for (DWORD i = 0; i < arrays[id].val.size(); i += step) { + sArrayElement &el = arrays[id].val[i + step - 1]; + if (el.type == datatype) { + if ((datatype != DATATYPE_STR && *(DWORD*)&(el.intVal) == val) || + (datatype == DATATYPE_STR && strcmp(el.strVal, (char*)val) == 0)) { + if (arrays[id].isAssoc()) { // return key instead of index for associative arrays + *resultType = getScriptTypeBySfallType(arrays[id].val[i].type); + return *(DWORD *)&arrays[id].val[i].intVal; + } else { + return i; + } + } + } + } + return -1; +} + +DWORD _stdcall LoadArray(DWORD key, DWORD keyType) { + int dataType = getSfallTypeByScriptType(keyType); + if (dataType != DATATYPE_INT || key != 0) { // returns arrayId by it's key (ignoring int(0) because it is used to "unsave" array) + sArrayElement keyEl = sArrayElement(key, dataType); + if (keyEl.type == DATATYPE_STR && strcmp(keyEl.strVal, get_all_arrays_special_key) == 0) { // this is a special case to get temp array containing all saved arrays + DWORD tmpArrId = TempArray(savedArrays.size(), 0); + if (tmpArrId > 0) { + std::vector::iterator elIt; + ArrayKeysMap::iterator it; + sArrayVar &tmpArr = arrays[tmpArrId]; + for (it = savedArrays.begin(), elIt = tmpArr.val.begin(); it != savedArrays.end(); ++it, ++elIt) { + elIt->set(it->first); + } + std::sort(tmpArr.val.begin(), tmpArr.val.end()); // sort in ascending order + return tmpArrId; + } + } else { + ArrayKeysMap::iterator it = savedArrays.find(keyEl); + if (it != savedArrays.end()) + return it->second; + } + } + return 0; // not found +} + +void _stdcall SaveArray(DWORD key, DWORD keyType, DWORD id) { + array_itr it = arrays.find(id), it2; + int dataType = getSfallTypeByScriptType(keyType); + if (it != arrays.end()) { + if (dataType != DATATYPE_INT || key != 0) { + // make array permanent + FixArray(it->first); + // if another array is saved under the same key, clear it + ArrayKeysMap::iterator sIt = savedArrays.find(sArrayElement(key, dataType)); + if (sIt != savedArrays.end() && sIt->second != id && (it2 = arrays.find(sIt->second)) != arrays.end()) { + it2->second.key.unset(); + } + it->second.key.setByType(key, dataType); + savedArrays[it->second.key] = id; + } else { // int(0) is used to "unsave" array without destroying it + int num = savedArrays.erase(it->second.key); + it->second.key.unset(); + } + } +} + +/* + Sets element to array created in last CreateArray call (used for array expressions) + For normal arrays, this method should be called in strict linear index order: + (create_array(0, 0) + arrayexpr(0, 20) + arrayexpr(1, 25) + arrayexpr(2, 777) + ...) + + For assoc arrays order doesn't matter: + (create_array(0, 1) + arrayexpr("key1", "val1") + arrayexpr("key2", 25) + arrayexpr(3, 6.1242) + ...) + + Should always return 0! +*/ +DWORD _stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType) { + DWORD id = stackArrayId; + if (id == 0 || arrays.find(id) == arrays.end()) return 0; + if (!arrays[id].isAssoc()) { + // automatically resize array to fit one new element + ResizeArray(id, arrays[id].size() + 1); + } + SetArray(id, key, keyType, val, valType, 0); + return 0; +} \ No newline at end of file diff --git a/sfall/Arrays.h b/sfall/Arrays.h new file mode 100644 index 00000000..09b1c57a --- /dev/null +++ b/sfall/Arrays.h @@ -0,0 +1,173 @@ +#pragma once +#include "main.h" +#include +#include +#include + +#define ARRAY_MAX_STRING (1024) // maximum length of string to be stored as array key or value +#define ARRAY_MAX_SIZE (100000) // maximum number of array elements, + // so total maximum memory/disk footprint of one array is: 16 + (ARRAY_MAX_STRING + 8) * ARRAY_MAX_SIZE + +// special actions for arrays using array_resize operator +#define ARRAY_ACTION_SORT (-2) +#define ARRAY_ACTION_RSORT (-3) +#define ARRAY_ACTION_REVERSE (-4) +#define ARRAY_ACTION_SHUFFLE (-5) + +extern char get_all_arrays_special_key[]; + +class sArrayElement +{ +public: + DWORD type; // DATATYPE_* + DWORD len; // for strings + union { + long intVal; + float floatVal; + char* strVal; + }; + sArrayElement(); + // this constructor does not copy strings (for performance), use set() for actual array elements + sArrayElement(DWORD _val, DWORD _dataType); + sArrayElement(const long&); + // free string resource from memory, has to be called manually when deleting element + void clear(); + // set* methods will actually COPY strings, use this when acquiring data from the scripting engine + void setByType(DWORD val, DWORD dataType); + void sArrayElement::set( const sArrayElement &el ) + { + setByType(el.intVal, el.type); + } + void set(long val); + void set(float val); + void set(const char* val, int _len = -1); + void unset(); + DWORD sArrayElement::getHash() const + { + return getHashStatic(*(DWORD*)&intVal, type); + } + DWORD static getHashStatic(DWORD value, DWORD type); + bool operator < (const sArrayElement &el) const; +}; + +class sArrayElement_HashFunc +{ +public: + size_t operator() (const sArrayElement &el) const { + return el.getHash(); + } +}; + +class sArrayElement_EqualFunc +{ +public: + bool operator() (const sArrayElement &elA, const sArrayElement &elB) const; +}; + +struct sArrayVarOld +{ + DWORD len; + DWORD datalen; + DWORD* types; + char* data; +}; + +#define ARRAYFLAG_ASSOC (1) // is map + +typedef std::tr1::unordered_map ArrayKeysMap; + +/** + This class represents sfall array + It can be both list (normal array) and map (associative) +*/ +class sArrayVar +{ +public: + DWORD flags; + sArrayElement key; // array associated key, if it was saved + ArrayKeysMap keyHash; // key element => element index, for faster lookup + std::vector val; // list of values or key=>value pairs (even - keys, odd - values) + + bool isAssoc() const { + return (flags & ARRAYFLAG_ASSOC); + } + // logical array size (number of elements for normal arrays; number of key=>value pairs for associative) + int size() const { + return isAssoc() + ? val.size() / 2 + : val.size(); + } + + // usefull when filling array from within sfall code (normal lists only) + void push_back(long _value) { + if (!isAssoc()) { + val.push_back(_value); + } + } + + sArrayVar() : flags(0), key() {} + // free memory used by strings + void clear() { + clearRange(0); + key.clear(); + } + + void clearRange(int from, int to = -1); +}; + +// arrays map: arrayId => arrayVar +typedef std::tr1::unordered_map ArraysMap; +extern ArraysMap arrays; +typedef ArraysMap::const_iterator array_citr; +typedef ArraysMap::iterator array_itr; +typedef std::pair array_pair; + +// auto-incremented ID +extern DWORD nextarrayid; +extern DWORD arraysBehavior; +// temp arrays: set of arrayId +extern std::set tempArrays; +// saved arrays: arrayKey => arrayId +extern ArrayKeysMap savedArrays; + +void LoadArraysOld(HANDLE h); +void LoadArrays(HANDLE h); +void SaveArrays(HANDLE h); +int GetNumArrays(); +void GetArrays(int* arrays); + +void DEGetArray(int id, DWORD* types, void* data); +void DESetArray(int id, const DWORD* types, const void* data); + +DWORD _stdcall getSfallTypeByScriptType(DWORD varType); +DWORD _stdcall getScriptTypeBySfallType(DWORD dataType); +// creates new normal (persistent) array. len == -1 specifies associative array (map) +DWORD _stdcall CreateArray(int len, DWORD nothing); +// same as CreateArray, but creates temporary array instead (will die at the end of the frame) +DWORD _stdcall TempArray(DWORD len, DWORD nothing); +// destroys array +void _stdcall FreeArray(DWORD id); +/* + op_get_array_key can be used to iterate over all keys in associative array +*/ +DWORD _stdcall GetArrayKey(DWORD id, int index, DWORD* resultType); +// get array element by index (list) or key (map) +DWORD _stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType); +// set array element by index or key +void _stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset); +// number of elements in list or pairs in map +int _stdcall LenArray(DWORD id); +// change array size (only works with list) +void _stdcall ResizeArray(DWORD id, int newlen); +// make temporary array persistent +void _stdcall FixArray(DWORD id); +// searches for a given element in array and returns it's index (for list) or key (for map) or int(-1) if not found +int _stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType); +// get saved array by it's key +DWORD _stdcall LoadArray(DWORD key, DWORD keyType); +// make array saved into the savegame with associated key +void _stdcall SaveArray(DWORD key, DWORD keyType, DWORD id); +// return keys of all saved arrays as a temp list +DWORD _stdcall SavedArrays(); +// special function that powers array expressions +DWORD _stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType); \ No newline at end of file diff --git a/sfall/BarBoxes.cpp b/sfall/BarBoxes.cpp new file mode 100644 index 00000000..88072598 --- /dev/null +++ b/sfall/BarBoxes.cpp @@ -0,0 +1,99 @@ +/* + * sfall + * Copyright (C) 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "BarBoxes.h" + +static const DWORD DisplayBoxesRet1=0x4615A8; +static const DWORD DisplayBoxesRet2=0x4615BE; +static const DWORD _add_bar_box=0x4616F0; +struct sBox { + DWORD msg; + DWORD colour; + void* mem; +}; +static sBox boxes[10]; +static DWORD boxesEnabled[5]; + +static void __declspec(naked) DisplayBoxesHook() { + __asm { + mov ebx, 0; +start: + mov eax, boxesEnabled[ebx*4]; + test eax, eax; + jz next; + lea eax, [ebx+5]; + call _add_bar_box; + add esi, eax; +next: + inc ebx; + cmp ebx, 4; + jne start; + cmp esi, 1; + jle fail; + jmp DisplayBoxesRet1; +fail: + jmp DisplayBoxesRet2; + } +} + +void BarBoxesInit() { + SafeWrite32(0x461266, (DWORD)boxes + 8); + SafeWrite32(0x4612AC, (DWORD)boxes + 8); + SafeWrite32(0x4612FE, (DWORD)boxes + 4); + SafeWrite32(0x46133C, (DWORD)boxes + 0); + SafeWrite32(0x461374, (DWORD)boxes + 8); + SafeWrite32(0x4613E8, (DWORD)boxes + 8); + + SafeWrite32(0x461479, (DWORD)boxes + 8); + SafeWrite32(0x46148C, (DWORD)boxes + 8); + SafeWrite32(0x4616BB, (DWORD)boxes + 8); + + memset(boxes, 0, 12*10); + memset(boxesEnabled, 0, 5*4); + memcpy(boxes, (void*)0x518FE8, 12*5); + + for(int i=5;i<10;i++) boxes[i].msg=0x69 + i - 5; + + SafeWrite8(0x46127C, 10); + SafeWrite8(0x46140B, 10); + SafeWrite8(0x461495, 0x78); + + MakeCall(0x4615A3, &DisplayBoxesHook, true); + char buf[6]; + GetPrivateProfileString("Misc", "BoxBarColours", "", buf, 6, ini); + if(strlen(buf)==5) { + for(int i=0;i<5;i++) { + if(buf[i]=='1') boxes[i+5].colour=1; + } + } +} + +int _stdcall GetBox(int i) { + if(i<5||i>9) return 0; + return boxesEnabled[i-5]; +} +void _stdcall AddBox(int i) { + if(i<5||i>9) return; + boxesEnabled[i-5]=1; +} +void _stdcall RemoveBox(int i) { + if(i<5||i>9) return; + boxesEnabled[i-5]=0; +} \ No newline at end of file diff --git a/sfall/BarBoxes.h b/sfall/BarBoxes.h new file mode 100644 index 00000000..c43bda6d --- /dev/null +++ b/sfall/BarBoxes.h @@ -0,0 +1,4 @@ +void BarBoxesInit(); +int _stdcall GetBox(int i); +void _stdcall AddBox(int i); +void _stdcall RemoveBox(int i); \ No newline at end of file diff --git a/sfall/Books.cpp b/sfall/Books.cpp new file mode 100644 index 00000000..e72adcdc --- /dev/null +++ b/sfall/Books.cpp @@ -0,0 +1,116 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" +#include + +#include "Books.h" + +static int BooksCount=0; +static const int BooksMax=30; +static char iniBooks[MAX_PATH]; + +struct sBook { + DWORD bookPid; + DWORD msgID; + DWORD skill; +}; + +static sBook books[BooksMax]; + +static sBook* _stdcall FindBook(DWORD pid) { + for (int i=0; i0) { + sprintf(iniBooks, ".\\%s", buf); + dlog("Applying books patch... ", DL_INIT); + memset(books,0,sizeof(sBook)*BooksCount); + + int i, n = 0, count; + if (GetPrivateProfileIntA("main", "overrideVanilla", 0, iniBooks) == 0) { + LoadVanillaBooks(); + } + count = GetPrivateProfileIntA("main", "count", 0, iniBooks); + + char section[4]; + for (i=1; i<=count; i++) { + _itoa_s(i, section, 10); + if (BooksCount >= BooksMax) break; + if (books[BooksCount].bookPid = GetPrivateProfileIntA(section, "PID", 0, iniBooks)) { + books[BooksCount].msgID = GetPrivateProfileIntA(section, "TextID", 0, iniBooks); + books[BooksCount].skill = GetPrivateProfileIntA(section, "Skill", 0, iniBooks); + n++; + BooksCount++; + } + } + + MakeCall(0x49B9FB, &obj_use_book_hook, true); + dlog_f(" (%d/%d books) Done\r\n", DL_INIT, n, count); + } +} \ No newline at end of file diff --git a/sfall/Books.h b/sfall/Books.h new file mode 100644 index 00000000..82489283 --- /dev/null +++ b/sfall/Books.h @@ -0,0 +1,21 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void BooksInit(); \ No newline at end of file diff --git a/sfall/BurstMods.cpp b/sfall/BurstMods.cpp new file mode 100644 index 00000000..349aeb2c --- /dev/null +++ b/sfall/BurstMods.cpp @@ -0,0 +1,82 @@ +/* + * sfall + * Copyright (C) 2008-2014 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Logging.h" + +static DWORD compute_spray_center_mult; +static DWORD compute_spray_center_div; +static DWORD compute_spray_target_mult; +static DWORD compute_spray_target_div; + +static const DWORD compute_spray_rounds_back = 0x42353A; +static void __declspec(naked) compute_spray_rounds_distribution() { + __asm { + // ebp - totalRounds + mov eax, ebp; // roundsCenter = totalRounds * mult / div + mov ebx, compute_spray_center_mult; + imul ebx; // multiply eax by ebx and store result in edx:eax + mov ebx, compute_spray_center_div; // divisor + idiv ebx; // divide edx:eax by ebx and store result in eax (edx) + mov [esp+16], eax; // roundsCenter + test eax, eax; // if (roundsCenter == 0) + jnz loc_42350F; + mov [esp+16], 1; // roundsCenter = 1; +loc_42350F: + mov eax, ebp; // roundsLeft = (totalRounds - roundsCenter) / 2 + sub eax, [esp+16]; // - roundsCenter + sar eax, 1; // /2 + mov [esp+8], eax; // roundsLeft + mov eax, ebp; // roundsRight = totalRounds - roundsCenter - roundsLeft + sub eax, [esp+16]; + sub eax, [esp+8]; + mov [esp+4], eax; // roundsRight + mov eax, [esp+16]; // roundsMainTarget = roundsCenter * mult / div + mov ebx, compute_spray_target_mult; + imul ebx; + mov ebx, compute_spray_target_div; + idiv ebx; + + mov ebp, eax; + mov ebx, [esp+16]; + // at this point, eax should contain the same value as ebp (roundsMainTarget); ebx should contain value of roundsCenter + jmp compute_spray_rounds_back; + } +} + + +void ComputeSprayModInit() { + if (GetPrivateProfileIntA("Misc", "ComputeSprayMod", 0, ini)) { + dlog("Applying ComputeSpray changes.", DL_INIT); + compute_spray_center_mult = GetPrivateProfileIntA("Misc", "ComputeSpray_CenterMult", 1, ini); + compute_spray_center_div = GetPrivateProfileIntA("Misc", "ComputeSpray_CenterDiv", 3, ini); + if (compute_spray_center_div < 1) + compute_spray_center_div = 1; + if (compute_spray_center_mult > compute_spray_center_div) + compute_spray_center_mult = compute_spray_center_div; + compute_spray_target_mult = GetPrivateProfileIntA("Misc", "ComputeSpray_TargetMult", 1, ini); + compute_spray_target_div = GetPrivateProfileIntA("Misc", "ComputeSpray_TargetDiv", 2, ini); + if (compute_spray_target_div < 1) + compute_spray_target_div = 1; + if (compute_spray_target_mult > compute_spray_target_div) + compute_spray_target_mult = compute_spray_target_div; + MakeCall(0x4234F1, &compute_spray_rounds_distribution, true); + dlogr(" Done", DL_INIT); + } +} \ No newline at end of file diff --git a/sfall/BurstMods.h b/sfall/BurstMods.h new file mode 100644 index 00000000..3211c1f8 --- /dev/null +++ b/sfall/BurstMods.h @@ -0,0 +1,21 @@ +/* + * sfall + * Copyright (C) 2008-2014 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void ComputeSprayModInit(); \ No newline at end of file diff --git a/sfall/CRC.cpp b/sfall/CRC.cpp new file mode 100644 index 00000000..5f442ec5 --- /dev/null +++ b/sfall/CRC.cpp @@ -0,0 +1,83 @@ +/* + * sfall + * Copyright (C) 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include "version.h" +#include "Logging.h" + +static const DWORD ExpectedSize=0x00122800; +static const DWORD ExpectedCRC[]= {0xe1680293, 0xef34f989}; + +static void inline Fail(const char* a) { MessageBoxA(0, a, "Error", MB_TASKMODAL); ExitProcess(1); } + +static DWORD crcInternal(BYTE* data, DWORD size) { + DWORD table[256]; + const DWORD kPoly = 0x1EDC6F41; + for (DWORD i = 0;i < 256;i++) { + DWORD r = i; + for (int j = 0;j < 8;j++) { + if ((r & 1) != 0) r = (r >> 1) ^ kPoly; + else r >>= 1; + } + table[i] = r; + } + + DWORD crc=0xffffffff; + for (DWORD i = 0;i < size;i++) { + crc = table[(((BYTE)(crc)) ^ data[i])] ^ (crc >> 8); + } + return crc^0xffffffff; +} + +void CRC(const char* filepath) { + char buf[512]; + HANDLE h = CreateFileA(filepath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (h == INVALID_HANDLE_VALUE) Fail("Cannot open fallout2.exe for CRC check"); + DWORD size = GetFileSize(h, 0), crc; + bool sizeMatch = (size == ExpectedSize); +#ifdef TRACE + if (!sizeMatch && GetPrivateProfileIntA("Debugging", "SkipSizeCheck", 0, ".\\ddraw.ini")) { + sizeMatch = true; + } +#endif + if (!sizeMatch) { + sprintf_s(buf, "You're trying to use sfall with an incompatible version of fallout\nWas expecting '" TARGETVERSION "'\n\nfallout2.exe was an unexpected size. Expected 0x%x but got 0x%x", ExpectedSize, size); + Fail(buf); + } + BYTE* bytes = new BYTE[size]; + ReadFile(h, bytes, size, &crc, 0); + crc = crcInternal(bytes, size); + + bool matchedCRC = false; +#ifdef TRACE + DWORD extraCRC = GetPrivateProfileIntA("Debugging", "ExtraCRC", 0, ".\\ddraw.ini"); + if (crc == extraCRC) matchedCRC = true; +#endif + for (int i=0; i < sizeof(ExpectedCRC)/4; i++) { + if (crc == ExpectedCRC[i]) matchedCRC = true; + } + if (!matchedCRC) { + sprintf_s(buf, "You're trying to use sfall with an incompatible version of fallout\nWas expecting '" TARGETVERSION "'\n\nfallout2.exe had an unexpected crc. Expected 0x%x but got 0x%x", ExpectedCRC[0], crc); + Fail(buf); + } + + CloseHandle(h); + delete[] bytes; +} diff --git a/sfall/CRC.h b/sfall/CRC.h new file mode 100644 index 00000000..3f4e0201 --- /dev/null +++ b/sfall/CRC.h @@ -0,0 +1,19 @@ +/* + * sfall + * Copyright (C) 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +void CRC(const char* filepath); \ No newline at end of file diff --git a/sfall/Console.cpp b/sfall/Console.cpp new file mode 100644 index 00000000..7f37eefc --- /dev/null +++ b/sfall/Console.cpp @@ -0,0 +1,83 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Console.h" +#include + +using namespace std; + +static ofstream consolefile; + +static void _stdcall ConsoleFilePrint(const char* msg) { + consolefile << msg << endl; +} + +static const DWORD ConsoleHookRet=0x431871; +static void __declspec(naked) ConsoleHook() { + __asm { + pushad; + push eax; + call ConsoleFilePrint; + popad; + push ebx; + push ecx; + push edx; + push esi; + push edi; + jmp ConsoleHookRet; + } +} + +void ConsoleInit() { + char path[MAX_PATH]; + GetPrivateProfileString("Misc", "ConsoleOutputPath", "", path, MAX_PATH, ini); + if(strlen(path)>0) { + consolefile.open(path); + if(consolefile.is_open()) MakeCall(0x43186C, &ConsoleHook, true); + } +} + +void ConsoleExit() { + if(consolefile.is_open()) consolefile.close(); +} + + + +/* An attempt to make popup display function.. failed to make it work, though all arguments are there. + I missed something... + +static const DWORD dialog_out_Addr = 0x41CF20; +void DisplayAlertPopup(const char* msg, const char* line2) { + __asm { + push 1 + mov al, ds:[0x6AB718] + push eax + push 0 + push eax + mov ecx, 0x0A9 + mov ebx, 2 + push 0x74 + mov edx, msg + mov eax, line2 + mov esi, 0FFFFFFFFh + mov edi, 2 + call dialog_out_Addr + } +}*/ \ No newline at end of file diff --git a/sfall/Console.h b/sfall/Console.h new file mode 100644 index 00000000..63ca51c4 --- /dev/null +++ b/sfall/Console.h @@ -0,0 +1,23 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void ConsoleExit(); +void ConsoleInit(); +//void DisplayAlertPopup(const char* msg, const char* line2); \ No newline at end of file diff --git a/sfall/Credits.cpp b/sfall/Credits.cpp new file mode 100644 index 00000000..5e1cd95a --- /dev/null +++ b/sfall/Credits.cpp @@ -0,0 +1,127 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Version.h" +#include + +static DWORD InCredits=0; +static DWORD CreditsLine=0; + +static const char* ExtraLines[]={ + "#SFALL " VERSION_STRING, + "", + "sfall is free software, licenced under the GPL", + "Copyright 2008-2015 The sfall team", + "", + "@Author", + "Timeslip", + "", + "@Contributors", + "ravachol", + "Noid", + "Glovz", + "Dream", + "Ray", + "Kanhef", + "KLIMaka", + "Mash", + "Helios", + "Haenlomal", + "NVShacker", + "NovaRain", + "JimTheDinosaur", + "phobos2077", + "Tehnokrat", + "", + "@Additional thanks to", + "Nirran", + "killap", + "MIB88", + "Rain man", + "Continuum", + "Anyone who has used sfall in their own mods", + "The bug reporters and feature requesters", + "" + "", + "", + "#FALLOUT 2", + "" +}; +static DWORD ExtraLineCount=sizeof(ExtraLines)/4; + +static const char* creditsFile="credits.txt"; + +static void _stdcall ShowCreditsHook() { + InCredits=1; + CreditsLine=0; + __asm { + mov eax, creditsFile; + mov ebx, 0x42C860; + call ebx; + } + InCredits=0; +} + +static DWORD _stdcall CreditsNextLine(char* buf, DWORD* font, DWORD* colour) { + if(!InCredits||CreditsLine>=ExtraLineCount) return 0; + const char* line=ExtraLines[CreditsLine++]; + if(strlen(line)) { + if(line[0]=='#') { + line++; + *font=*(DWORD*)0x56D74C; + *colour=*(BYTE*)0x6A7F01; + } else if(line[0]=='@') { + line++; + *font=*(DWORD*)0x56D748; + *colour=*(DWORD*)0x56D750; + } else { + *font=*(DWORD*)0x56D74C; + *colour=*(DWORD*)0x56D744; + } + } + strcpy_s(buf, 256, line); + return 1; +} + +static const DWORD _credits_get_next_line=0x42CE6C; +static void __declspec(naked) CreditsNextLineHook() { + __asm { + pushad; + push ebx; + push edx; + push eax; + call CreditsNextLine; + test eax, eax; + jz fail; + popad; + xor eax, eax; + inc eax; + retn; +fail: + popad; + jmp _credits_get_next_line; + } +} + +void CreditsInit() { + HookCall(0x480C49, &ShowCreditsHook); + HookCall(0x43F881, &ShowCreditsHook); + HookCall(0x42CB49, &CreditsNextLineHook); +} \ No newline at end of file diff --git a/sfall/Credits.h b/sfall/Credits.h new file mode 100644 index 00000000..0b86d705 --- /dev/null +++ b/sfall/Credits.h @@ -0,0 +1,21 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void CreditsInit(); \ No newline at end of file diff --git a/sfall/Criticals.cpp b/sfall/Criticals.cpp new file mode 100644 index 00000000..f1bba81b --- /dev/null +++ b/sfall/Criticals.cpp @@ -0,0 +1,253 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Criticals.h" +#include "Logging.h" +#include + +static const DWORD CritTableCount=2*19+1; //Number of species in new critical table +//static const DWORD origCritTableSize = 6*9*20; //Number of entries in original table +static const DWORD CritTableSize = 6*9*CritTableCount; //Number of entries in new critical table +static DWORD mode; + +static const char* CritNames[] = { + "DamageMultiplier", + "EffectFlags", + "StatCheck", + "StatMod", + "FailureEffect", + "Message", + "FailMessage", +}; + +struct CritStruct { + union { + struct { + DWORD DamageMultiplier; + DWORD EffectFlags; + DWORD StatCheck; + DWORD StatMod; + DWORD FailureEffect; + DWORD Message; + DWORD FailMessage; + }; + DWORD values[7]; + }; +}; + +static CritStruct* critTable; +static CritStruct* playerCrit; +static bool Inited=false; + +void _stdcall SetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element, DWORD value) { + if(!Inited) return; + if(critter>=CritTableCount||bodypart>=9||slot>=6||element>=7) return; + critTable[critter*9*6+bodypart*6+slot].values[element]=value; +} +DWORD _stdcall GetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element) { + if(!Inited) return 0; + if(critter>=CritTableCount||bodypart>=9||slot>=6||element>=7) return 0; + return critTable[critter*9*6+bodypart*6+slot].values[element]; +} +void _stdcall ResetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element) { + if(!Inited) return; + if(critter>=CritTableCount||bodypart>=9||slot>=6||element>=7) return; + //It's been a long time since we worried about win9x compatibility, so just sprintf it for goodness sake... + char section[16]; + sprintf_s(section, "c_%02d_%d_%d", critter, bodypart, slot); + CritStruct* defaultTable=(CritStruct*)0x510978; + critTable[slot].values[element]=critTable[slot].DamageMultiplier=GetPrivateProfileIntA(section, CritNames[element], defaultTable[slot].values[element], ".\\CriticalOverrides.ini"); +} + +void CritLoad() { + if(!Inited) return; + CritStruct* defaultTable=(CritStruct*)0x510978; + if(mode==1) { + char section[16]; + dlogr("Setting up critical hit table using CriticalOverrides.ini", DL_CRITICALS); + memset(critTable, 0, CritTableSize*sizeof(CritStruct)); + for(DWORD critter=0;critter<20;critter++) { + for(DWORD part=0;part<9;part++) { + for(DWORD crit=0;crit<6;crit++) { + sprintf_s(section, "c_%02d_%d_%d", critter, part, crit); + int slot1=crit+part*6+critter*9*6; + int slot2=crit+part*6+((critter==19)?38:critter)*9*6; + for(int i=0;i<7;i++) { + critTable[slot2].values[i]=GetPrivateProfileIntA(section, CritNames[i], defaultTable[slot1].values[i], ".\\CriticalOverrides.ini"); +#ifdef TRACE + char logmsg[256]; + if(critTable[slot2].values[i]!=defaultTable[slot1].values[i]) { + sprintf_s(logmsg, "Entry %s value %d changed from %d to %d", section, i, defaultTable[slot1].values[i], critTable[slot2].values[i]); + dlogr(logmsg, DL_CRITICALS); + } +#endif + } + } + } + } + } else { + dlogr("Setting up critical hit table using RP fixes", DL_CRITICALS); + memcpy(critTable, defaultTable, 6*9*19*sizeof(CritStruct)); + memset(&critTable[6*9*19], 0, 6*9*19*sizeof(CritStruct)); + memcpy(playerCrit, (void*)0x5179B0, 6*9*sizeof(CritStruct)); + + if(mode==3) { + char buf[32], buf2[32], buf3[32]; + for(int critter=0;critter3) mode=0; + + if(!mode) return; + + dlog("Initilizing critical table override.", DL_INIT); + critTable=new CritStruct[CritTableSize]; + playerCrit=&critTable[6*9*38]; + SafeWrite32(0x423F96, (DWORD)playerCrit); + SafeWrite32(0x423FB3, (DWORD)critTable); + dlog(". ", DL_INIT); + + if(mode==2 || mode==3) { + CritStruct* defaultTable=(CritStruct*)0x510978; + + SetEntry(2,4,1,4,0); + SetEntry(2,4,1,5,5216); + SetEntry(2,4,1,6,5000); + + SetEntry(2,4,2,4,0); + SetEntry(2,4,2,5,5216); + SetEntry(2,4,2,6,5000); + + SetEntry(2,5,1,4,0); + SetEntry(2,5,1,5,5216); + SetEntry(2,5,1,6,5000); + + SetEntry(2,5,2,4,0); + SetEntry(2,5,2,5,5216); + SetEntry(2,5,2,6,5000); + + SetEntry(3,5,1,6,5306); + + SetEntry(4,0,4,2,-1); + + SetEntry(5,0,4,2,-1); + + SetEntry(6,4,1,4,2); + + SetEntry(6,5,1,4,2); + + SetEntry(6,5,2,6,5608); + + SetEntry(9,3,3,4,2); + + SetEntry(13,5,1,4,4); + SetEntry(13,5,2,4,4); + SetEntry(13,5,3,4,4); + SetEntry(13,5,4,4,4); + SetEntry(13,5,5,4,4); + + SetEntry(18,0,0,5,5001); + SetEntry(18,0,1,5,5001); + SetEntry(18,0,2,5,5001); + SetEntry(18,0,3,5,7105); + SetEntry(18,0,4,5,7101); + SetEntry(18,0,4,6,7104); + SetEntry(18,0,5,5,7101); + + SetEntry(18,1,0,5,5008); + SetEntry(18,1,1,5,5008); + SetEntry(18,1,2,5,5009); + SetEntry(18,1,3,5,5009); + SetEntry(18,1,4,5,7102); + SetEntry(18,1,5,5,7102); + + SetEntry(18,2,0,5,5008); + SetEntry(18,2,1,5,5008); + SetEntry(18,2,2,5,5009); + SetEntry(18,2,3,5,5009); + SetEntry(18,2,4,5,7102); + SetEntry(18,2,5,5,7102); + + SetEntry(18,3,4,5,7101); + SetEntry(18,3,5,5,7101); + + SetEntry(18,4,0,5,5023); + SetEntry(18,4,1,5,7101); + SetEntry(18,4,1,6,7103); + SetEntry(18,4,2,5,7101); + SetEntry(18,4,2,6,7103); + SetEntry(18,4,3,5,7103); + SetEntry(18,4,4,5,7103); + SetEntry(18,4,5,5,7103); + + SetEntry(18,5,0,5,5023); + SetEntry(18,5,1,5,7101); + SetEntry(18,5,1,6,7103); + SetEntry(18,5,2,5,7101); + SetEntry(18,5,2,6,7103); + SetEntry(18,5,3,5,7103); + SetEntry(18,5,4,5,7103); + SetEntry(18,5,5,5,7103); + + SetEntry(18,6,0,5,5027); + SetEntry(18,6,1,5,5027); + SetEntry(18,6,2,5,5027); + //SetEntry(18,6,2,6,0); + SetEntry(18,6,3,5,5027); + SetEntry(18,6,4,5,7104); + SetEntry(18,6,5,5,7104); + + SetEntry(18,7,0,5,5033); + SetEntry(18,7,1,5,5027); + SetEntry(18,7,1,6,7101); + SetEntry(18,7,2,5,7101); + SetEntry(18,7,3,5,7101); + SetEntry(18,7,4,5,7101); + SetEntry(18,7,5,5,7101); + } + + Inited=true; + dlogr(" Done", DL_INIT); +} \ No newline at end of file diff --git a/sfall/Criticals.h b/sfall/Criticals.h new file mode 100644 index 00000000..3d9c021b --- /dev/null +++ b/sfall/Criticals.h @@ -0,0 +1,26 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void CritInit(); +void CritLoad(); + +void _stdcall SetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element, DWORD value); +DWORD _stdcall GetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element); +void _stdcall ResetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element); \ No newline at end of file diff --git a/sfall/DebugEditor.cpp b/sfall/DebugEditor.cpp new file mode 100644 index 00000000..57ecb5b4 --- /dev/null +++ b/sfall/DebugEditor.cpp @@ -0,0 +1,238 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "DebugEditor.h" +#include "ScriptExtender.h" +#include "Arrays.h" +#include + +#define CODE_EXIT (254) +#define CODE_SET_GLOBAL (0) +#define CODE_SET_MAPVAR (1) +#define CODE_GET_CRITTER (2) +#define CODE_SET_CRITTER (3) +#define CODE_SET_SGLOBAL (4) +#define CODE_GET_PROTO (5) +#define CODE_SET_PROTO (6) +#define CODE_GET_PLAYER (7) +#define CODE_SET_PLAYER (8) +#define CODE_GET_ARRAY (9) +#define CODE_SET_ARRAY (10) + +static bool SetBlocking(SOCKET s, bool block) { + DWORD d=!block; + ioctlsocket(s, FIONBIO, &d); +} +static bool InternalSend(SOCKET s, const void* _data, int size) { + const char* data=(const char*) _data; + int upto=0; + int tmp; + DWORD d; + while(upto0) upto+=tmp; + else { + d=WSAGetLastError(); + if(d!=WSAEWOULDBLOCK && d!=WSAENOBUFS) return true; + } + } + return false; +} +static bool InternalRecv(SOCKET s, void* _data, int size) { + char* data=(char*)_data; + int upto=0; + int tmp; + DWORD d; + while(upto0) upto+=tmp; + else { + d=WSAGetLastError(); + if(d!=WSAEWOULDBLOCK && d!=WSAENOBUFS) return true; + } + } + return false; +} +static const DWORD obj_find_first_at_tile=0x48B5A8; +static const DWORD obj_find_next_at_tile=0x48B608; +static void RunEditorInternal(SOCKET &s) { + std::vector vec = std::vector(); + for(int elv=0;elv<3;elv++) { + for(int tile=0;tile<40000;tile++) { + DWORD* obj; + __asm { + mov edx, tile; + mov eax, elv; + call obj_find_first_at_tile; + mov obj, eax; + } + while(obj) { + DWORD otype = obj[25]; + otype = (otype&0xff000000) >> 24; + if(otype==1) vec.push_back(obj); + __asm { + call obj_find_next_at_tile; + mov obj, eax; + } + } + } + } + + int numCritters=vec.size(); + + int numGlobals=*(int*)0x5186C4; + int numMapVars=*(int*)0x519574; + int numSGlobals=GetNumGlobals(); + int numArrays=GetNumArrays(); + InternalSend(s, &numGlobals, 4); + InternalSend(s, &numMapVars, 4); + InternalSend(s, &numSGlobals, 4); + InternalSend(s, &numArrays, 4); + InternalSend(s, &numCritters, 4); + + sGlobalVar* sglobals=new sGlobalVar[numSGlobals]; + GetGlobals(sglobals); + int* arrays=new int[numArrays*3]; + GetArrays(arrays); + + InternalSend(s, *(void**)0x5186C0, 4*numGlobals); + InternalSend(s, *(void**)0x51956C, 4*numMapVars); + InternalSend(s, sglobals, sizeof(sGlobalVar)*numSGlobals); + InternalSend(s, arrays, numArrays*3*4); + for(int i=0;i. + */ + +#include "main.h" + +#include "Elevators.h" + +static const int ElevatorCount=50; +static char File[MAX_PATH]; + +struct sElevator { + DWORD ID1; + DWORD Elevation1; + DWORD Tile1; + DWORD ID2; + DWORD Elevation2; + DWORD Tile2; + DWORD ID3; + DWORD Elevation3; + DWORD Tile3; + DWORD ID4; + DWORD Elevation4; + DWORD Tile4; +}; + +static sElevator Elevators[ElevatorCount]; +static DWORD Menus[ElevatorCount]; + +void SetElevator(DWORD id, DWORD index, DWORD value) { + if(id>=ElevatorCount||index>=12) return; + *(DWORD*)(((DWORD)&Elevators[id])+index*4)=value; +} + +static const DWORD GetMenuAddress=0x43F324; +static void __declspec(naked) GetMenuHook() { + __asm { + push ebx; + lea ebx, Menus; + shl eax, 2; + mov eax, [ebx+eax]; + call GetMenuAddress; + pop ebx; + ret; + } +} + +static const DWORD UnknownAddress=0x43F73C; +static void __declspec(naked) UnknownHook() { + __asm { + push ebx; + lea ebx, Menus; + shl eax, 2; + mov eax, [ebx+eax]; + call UnknownAddress; + pop ebx; + ret; + } +} +static const DWORD UnknownAddress2=0x43F6D0; +static void __declspec(naked) UnknownHook2() { + __asm { + push ebx; + lea ebx, Menus; + shl eax, 2; + mov eax, [ebx+eax]; + call UnknownAddress2; + pop ebx; + ret; + } +} + +static void __declspec(naked) GetNumButtonsHook1() { + __asm { + lea esi, Menus; + mov eax, [esi+edi*4]; + mov eax, [0x43EA1C+eax*4]; + mov esi, 0x43F064; + jmp esi; + } +} +static void __declspec(naked) GetNumButtonsHook2() { + __asm { + lea edx, Menus; + mov eax, [edx+edi*4]; + mov eax, [0x43EA1C+eax*4]; + mov edx, 0x43F18B; + jmp edx; + } +} +static void __declspec(naked) GetNumButtonsHook3() { + __asm { + lea eax, Menus; + mov eax, [eax+edi*4]; + mov eax, [0x43EA1C+eax*4]; + mov ebx, 0x43F1EB; + jmp ebx; + } +} + +void ResetElevators() { + memcpy(Elevators, (void*)0x43EA7C, sizeof(sElevator)*24); + memset(&Elevators[24], 0, sizeof(sElevator)*(ElevatorCount-24)); + for(int i=0;i<24;i++) Menus[i]=i; + for(int i=24;i. + */ + +#pragma once + +void ElevatorsInit(char* file); \ No newline at end of file diff --git a/sfall/Explosions.cpp b/sfall/Explosions.cpp new file mode 100644 index 00000000..8651f84c --- /dev/null +++ b/sfall/Explosions.cpp @@ -0,0 +1,218 @@ +/* + * sfall + * Copyright (C) 2008-2014 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Logging.h" +#include "ScriptExtender.h" +#include "FalloutEngine.h" + +static bool lightingEnabled = false; + +static const DWORD ranged_attack_lighting_fix_back = 0x4118F8; + +// enable lighting for flying projectile, based on projectile PID data (light intensity & radius) +static void __declspec(naked) ranged_attack_lighting_fix() { + __asm { + mov eax, [esp+40]; // projectile ptr - 1st arg + mov ecx, [esp+1Ch]; // protoPtr of projectile + mov edx, [ecx+0x0C]; // light radius - 2nd arg + mov ebx, [ecx+0x10]; // light intensity - 4th arg + //mov ebx, 0x10000; // light intensity - 4th arg + xor ecx, ecx; // unknown(0) - 3rd argument + call obj_set_light_; + jmp ranged_attack_lighting_fix_back; // jump back + } +} + +// misc functions from the engine.. +static const DWORD register_object_play_sfx = 0x41541C; +static const DWORD gsnd_build_weapon_sfx_name_ = 0x451760; + +static const DWORD explosion_effect_hook_back = 0x411AB9; +static DWORD explosion_effect_starting_dir = 0; +static void __declspec(naked) explosion_effect_hook() { + __asm { + mov bl, lightingEnabled + test bl, bl + jz usevanilla + xor ebx, ebx + call register_object_animate_ + jmp next +usevanilla: + xor ebx, ebx + call register_object_animate_and_hide_ +next: + mov al, lightingEnabled + test al, al + jz skiplight + mov eax, [esp+40]; // projectile ptr - 1st arg + mov edx, 0xFFFF0008; // maximum radius + intensity (see anim_set_check__light_fix) + mov ebx, 0 + call register_object_light_; +skiplight: + mov edi, explosion_effect_starting_dir; // starting direction + jmp explosion_effect_hook_back; // jump back + } +} + +static const DWORD explosion_lighting_fix2_back = 0x412FC7; +// enable lighting for central explosion object (action_explode) +static void __declspec(naked) explosion_lighting_fix2() { + __asm { + mov eax, [esp+24] + mov edx, 1 // turn on + mov ebx, 0 + call register_object_funset_ + + mov eax, [esp+24]; // explosion obj ptr + mov edx, 0xFFFF0008; // maximum radius + intensity (see anim_set_check__light_fix) + mov ebx, 0 + call register_object_light_; + + mov eax, [esp+24] + xor edx, edx + mov ebx, 0 + call register_object_animate_ + + jmp explosion_lighting_fix2_back; // jump back + } +} + + +DWORD _stdcall LogThis(DWORD value1, DWORD value2, DWORD value3) { + dlog_f("anim_set_check__light_fix: object 0x%X, something 0x%X, radius 0x%X", DL_MAIN, value1, value2, value3); + return value1; +} + +static const DWORD anim_set_check__light_back = 0x415A4C; +static void __declspec(naked) anim_set_check__light_fix() { + __asm { + mov eax, [esi+4] // object + lea ecx, [esp+16] // unknown.. something related to next "tile_refresh_rect" call? + mov edx, [esi+12] // radius set in "reg_anim_light" + mov ebx, edx + shr ebx, 16 // take highest 2 bytes + test ebx, ebx + jz nothingspecial + // special case + and edx, 0xFF // use lowest byte as radius, highest 2 bytes as intensity + inc ebx + jmp end +nothingspecial: + mov ebx, [eax+112] // object current light intensity (original behavior) +end: + jmp anim_set_check__light_back; // jump back right to the "obj_set_light" call + } +} + +static const DWORD fire_dance_lighting_back = 0x410A4F; +// enable lighting for burning poor guy +static void __declspec(naked) fire_dance_lighting_fix1() { + __asm { + push edx; + push ebx; + mov eax, esi; // projectile ptr - 1st arg + mov edx, 0xFFFF0002; // maximum radius + intensity (see anim_set_check__light_fix) + mov ebx, 0 + call register_object_light_; + mov eax, esi; + pop ebx; + pop edx; + call register_object_animate_; // overwritten call + mov eax, esi; // projectile ptr - 1st arg + mov edx, 0x00010000; // maximum radius + intensity (see anim_set_check__light_fix) + mov ebx, -1 + call register_object_light_; + + jmp fire_dance_lighting_back; // jump back + } +} + + + +static const DWORD explosion_dmg_check_adr[] = {0x411709, 0x4119FC, 0x411C08, 0x4517C1, 0x423BC8}; +static const DWORD explosion_art_adr[] = {0x411A19, 0x411A29, 0x411A35, 0x411A3C}; +static const DWORD explosion_art_defaults[] = {10, 2, 31, 29}; +static const DWORD explosion_radius_grenade = 0x479183; +static const DWORD explosion_radius_rocket = 0x47918B; +#define EXPL_FORCE_EXPLOSION_PATTERN (1) +#define EXPL_FORCE_EXPLOSION_ART (2) +#define EXPL_FORCE_EXPLOSION_RADIUS (3) +#define EXPL_FORCE_EXPLOSION_DMGTYPE (4) +DWORD _stdcall ExplosionsMetaruleFunc(DWORD mode, DWORD arg1, DWORD arg2) { + int i; + switch (mode) { + case EXPL_FORCE_EXPLOSION_PATTERN: + if (arg1) { + explosion_effect_starting_dir = 2; // bottom-right + SafeWrite8(0x411B54, 4); // bottom-left + 1 + } else { + explosion_effect_starting_dir = 0; + SafeWrite8(0x411B54, 6); // last direction + } + break; + case EXPL_FORCE_EXPLOSION_ART: + for (i=0; i<4; i++) { + SafeWrite32(explosion_art_adr[i], (BYTE)arg1); + } + break; + case EXPL_FORCE_EXPLOSION_RADIUS: + SafeWrite32(explosion_radius_grenade, arg1); + SafeWrite32(explosion_radius_rocket, arg1); + break; + case EXPL_FORCE_EXPLOSION_DMGTYPE: + for (i=0; i<4; i++) { + SafeWrite8(explosion_dmg_check_adr[i], (BYTE)arg1); + } + break; + } + return 0; +} + +void ResetExplosionSettings() { + int i; + // explosion pattern + explosion_effect_starting_dir = 0; + SafeWrite8(0x411B54, 6); // last direction + // explosion art + for (i=0; i<4; i++) { + SafeWrite32(explosion_art_adr[i], explosion_art_defaults[i]); + } + // explosion radiuses + SafeWrite32(explosion_radius_grenade, 2); + SafeWrite32(explosion_radius_rocket, 3); + // explosion dmgtype + for (i=0; i<4; i++) { + SafeWrite8(explosion_dmg_check_adr[i], 6); + } +} + +void ExplosionLightingInit() { + MakeCall(0x411AB4, &explosion_effect_hook, true); // required for explosions_metarule + + if (GetPrivateProfileIntA("Misc", "ExplosionsEmitLight", 0, ini)) { + dlog("Initing Explosion changes.", DL_INIT); + lightingEnabled = true; + MakeCall(0x4118E1, &ranged_attack_lighting_fix, true); + MakeCall(0x410A4A, &fire_dance_lighting_fix1, true); + MakeCall(0x415A3F, anim_set_check__light_fix, true); // this allows to change light intensity + + dlogr(" Done", DL_INIT); + } +} \ No newline at end of file diff --git a/sfall/Explosions.h b/sfall/Explosions.h new file mode 100644 index 00000000..2dfbe8b6 --- /dev/null +++ b/sfall/Explosions.h @@ -0,0 +1,23 @@ +/* + * sfall + * Copyright (C) 2008-2014 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void ExplosionLightingInit(); +DWORD _stdcall ExplosionsMetaruleFunc(DWORD mode, DWORD arg1, DWORD arg2); +void ResetExplosionSettings(); \ No newline at end of file diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp new file mode 100644 index 00000000..273c817f --- /dev/null +++ b/sfall/FalloutEngine.cpp @@ -0,0 +1,162 @@ +/* + * sfall + * Copyright (C) 2008-2015 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Logging.h" +#include "FalloutEngine.h" + +// global variables +TGameObj** obj_dude_ptr = (TGameObj**)(0x6610B8); +TGameObj** inven_dude_ptr = (TGameObj**)(0x519058); +DWORD* activeUIHand_ptr = (DWORD*)(0x518F78); // 0 - left, 1 - right +DWORD* dude_traits = (DWORD*)(0x66BE40); // 2 of them +DWORD* itemCurrentItem = (DWORD*)(0x518F78); +DWORD* itemButtonItems = (DWORD*)(0x5970F8); + +/** + ENGINE FUNCTIONS OFFSETS + const names should end with underscore +*/ + +// INVENTORY FUNCTIONS +const DWORD inven_left_hand_ = 0x471BBC; // eax - object +const DWORD inven_right_hand_ = 0x471B70; // eax - object + +// PROTO FUNCTIONS +const DWORD proto_ptr_ = 0x4A2108; // eax - PID, edx - int** - pointer to a pointer to a proto struct +const DWORD item_get_type_ = 0x477AFC; // eax - TGameObj* item +const DWORD item_w_max_ammo_ = 0x478674; // eax - object +const DWORD item_w_cur_ammo_ = 0x4786A0; // eax - object + +// AI FUNCTIONS +const DWORD ai_can_use_weapon_ = 0x4298EC; // (TGameObj *aCritter, int aWeapon, int a2Or3) returns 1 or 0 + +// UI FUNCTIONS +const DWORD isPartyMember_ = 0x494FC4; // eax - object +const DWORD intface_redraw_ = 0x45EB98; +const DWORD interface_disable_ = 0x45EAFC; +const DWORD interface_enable_ = 0x45EA64; +const DWORD intface_toggle_items_ = 0x45F404; +const unsigned int display_print_ = 0x43186C; +const DWORD intface_item_reload_ = 0x460B20; +const DWORD intface_toggle_item_state_ = 0x45F4E0; +const DWORD intface_use_item_ = 0x45F5EC; + +// OBJECTS manipulation +const DWORD obj_set_light_ = 0x48AC90; // (int aObj, signed int aDist, int a3, int aIntensity) +const DWORD obj_new_ = 0x489A84; // int aObj*, int aPid +const DWORD obj_turn_off_ = 0x48AE68; // int aObj, int ??? +const DWORD obj_move_to_tile_ = 0x48A568; // int aObj, int aTile, int aElev + +const DWORD obj_find_first_at_tile_ = 0x48B5A8; // (int elevation, int tile) +const DWORD obj_find_next_at_tile_ = 0x48B608; // no args +const DWORD critter_is_dead_ = 0x42DD18; // eax - critter + +// ANIMATION +const DWORD tile_refresh_rect_ = 0x4B12C0; // (int elevation, unkown) +const DWORD register_object_animate_ = 0x4149D0; // int aObj, int aAnim, int delay +const DWORD register_object_animate_and_hide_ = 0x414B7C; // int aObj, int aAnim, int delay +const DWORD register_object_must_erase_ = 0x414E20; // int aObj +const DWORD register_object_change_fid_ = 0x41518C; // int aObj, int aFid, int aDelay +const DWORD register_object_light_ = 0x415334; // (int aObj, int aRadius, int aDelay) +const DWORD register_object_funset_ = 0x4150A8; // int aObj, int ???, int aDelay - not really sure what this does +const DWORD register_object_take_out_ = 0x415238; // int aObj, int aHoldFrame - hold frame ID (1 - spear, 2 - club, etc.) +const DWORD register_object_turn_towards_ = 0x414C50; // int aObj, int aTile + +// ART +const DWORD art_exists_ = 0x4198C8; // eax - frameID, used for critter FIDs + +// misc +static const DWORD getmsg_ = 0x48504C; // eax - msg file addr, ebx - message ID, edx - int[4] - loads string from MSG file preloaded in memory + +// WRAPPERS +// please, use CamelCase for those + + +int __stdcall ItemGetType(TGameObj* item) { + __asm { + mov eax, item; + call item_get_type_; + } +} + +int _stdcall IsPartyMember(TGameObj* obj) { + __asm { + mov eax, obj; + call isPartyMember_; + } +} + +TGameObj* GetInvenWeaponLeft(TGameObj* obj) { + __asm { + mov eax, obj; + call inven_left_hand_; + } +} + +TGameObj* GetInvenWeaponRight(TGameObj* obj) { + __asm { + mov eax, obj; + call inven_right_hand_; + } +} + + +char* GetProtoPtr(DWORD pid) { + char* proto; + __asm { + mov eax, pid; + lea edx, proto; + call proto_ptr_; + } + return proto; +} + +char AnimCodeByWeapon(TGameObj* weapon) { + if (weapon != NULL) { + char* proto = GetProtoPtr(weapon->pid); + if (proto && *(int*)(proto + 32) == 3) { + return (char)(*(int*)(proto + 36)); + } + } + return 0; +} + + +void DisplayConsoleMessage(const char* msg) { + __asm { + mov eax, msg; + call display_print_; + } +} + +static DWORD mesg_buf[4] = {0, 0, 0, 0}; +const char* _stdcall GetMessageStr(DWORD fileAddr, DWORD messageId) +{ + DWORD buf = (DWORD)mesg_buf; + const char* result; + __asm { + mov eax, fileAddr + mov ebx, messageId + mov edx, buf + call getmsg_ + mov result, eax + } + return result; +} diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h new file mode 100644 index 00000000..5d139c3e --- /dev/null +++ b/sfall/FalloutEngine.h @@ -0,0 +1,120 @@ +/* +* sfall +* Copyright (C) 2008-2015 The sfall team +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#pragma once + +/* +* FALLOUT2.EXE structs, function offsets and wrappers should be placed here +* +* only place functions and variables here which are likely to be used in more than one module +* +*/ + +#include "FalloutStructs.h" + +extern TGameObj** obj_dude_ptr; +extern TGameObj** inven_dude_ptr; +extern DWORD* activeUIHand_ptr; // 0 - left, 1 - right +extern DWORD* dude_traits; // 2 of them +extern DWORD* itemCurrentItem; +extern DWORD* itemButtonItems; + +/* +* HOW TO USE ENGINE FUNCTIONS: +* +* in ASM code, call offsets directly, don't call wrappers as they might not be _stdcall +* in C++ code, use wrappers (add new ones if the don't exist yet) +* +* Note: USE C++! +* 1) Place thin __declspec(naked) hooks, only use minimum ASM to pass values to/from C++ +* 2) Call _stdcall functions from (1), write those entirely in C++ (with little ASM blocks only to call engine functions, when you are too lazy to add wrapper) +*/ + +extern const DWORD item_get_type_; +extern const DWORD inven_left_hand_; // eax - object +extern const DWORD inven_right_hand_; // eax - object +extern const DWORD proto_ptr_; // eax - PID, edx - int** - pointer to a pointer to a proto struct +extern const DWORD ai_can_use_weapon_; // (TGameObj *aCritter, int aWeapon, int a2Or3) returns 1 or 0 +extern const DWORD item_w_max_ammo_; // eax - object +extern const DWORD item_w_cur_ammo_; // eax - object + + +// Interface +extern const DWORD intface_redraw_; // no args +extern const DWORD interface_disable_; +extern const DWORD interface_enable_; +extern const DWORD intface_toggle_items_; +extern const unsigned int display_print_; // eax - char* to display +extern const DWORD intface_item_reload_; // no args +extern const DWORD intface_toggle_item_state_; // no args +extern const DWORD intface_use_item_; // no args + +// objects +extern const DWORD isPartyMember_; // ( - object) - bool result +extern const DWORD obj_set_light_; // (int aObj, signed int aDist, int a3, int aIntensity) +extern const DWORD obj_new_; // int aObj*, int aPid +extern const DWORD obj_turn_off_; // int aObj, int ??? +extern const DWORD obj_move_to_tile_; // int aObj, int aTile, int aElev +extern const DWORD obj_find_first_at_tile_; // (int elevation, int tile) +extern const DWORD obj_find_next_at_tile_; // no args +extern const DWORD critter_is_dead_; // eax - critter + +// Animation +extern const DWORD tile_refresh_rect_; // (int elevation, unkown) +extern const DWORD register_object_animate_; // int aObj, int aAnim, int delay +extern const DWORD register_object_animate_and_hide_; // int aObj, int aAnim, int delay +extern const DWORD register_object_must_erase_; // int aObj +extern const DWORD register_object_change_fid_; // int aObj, int aFid, int aDelay +extern const DWORD register_object_light_; // (int aObj, int aRadius, int aDelay) +extern const DWORD register_object_funset_; // int aObj, int ???, int aDelay +extern const DWORD register_object_take_out_; // int aObj, int aHoldFrame - hold frame ID (1 - spear, 2 - club, etc.) +extern const DWORD register_object_turn_towards_; // int aObj, int aTile + +extern const DWORD art_exists_; // eax - frameID, used for critter FIDs + +extern const DWORD getmsg_; // eax - msg file addr, ebx - message ID, edx - int[4] - loads string from MSG file preloaded in memory + +#define MSG_FILE_COMBAT (0x56D368) +#define MSG_FILE_AI (0x56D510) +#define MSG_FILE_SCRNAME (0x56D754) +#define MSG_FILE_MISC (0x58E940) +#define MSG_FILE_CUSTOM (0x58EA98) +#define MSG_FILE_INVENTRY (0x59E814) +#define MSG_FILE_ITEM (0x59E980) +#define MSG_FILE_LSGAME (0x613D28) +#define MSG_FILE_MAP (0x631D48) +#define MSG_FILE_OPTIONS (0x6637E8) +#define MSG_FILE_PERK (0x6642D4) +#define MSG_FILE_PIPBOY (0x664348) +#define MSG_FILE_QUESTS (0x664410) +#define MSG_FILE_PROTO (0x6647FC) +#define MSG_FILE_SCRIPT (0x667724) +#define MSG_FILE_SKILL (0x668080) +#define MSG_FILE_SKILLDEX (0x6680F8) +#define MSG_FILE_STAT (0x66817C) +#define MSG_FILE_TRAIT (0x66BE38) +#define MSG_FILE_WORLDMAP (0x672FB0) + +// WRAPPERS: +int _stdcall IsPartyMember(TGameObj* obj); +TGameObj* GetInvenWeaponLeft(TGameObj* obj); +TGameObj* GetInvenWeaponRight(TGameObj* obj); +char* GetProtoPtr(DWORD pid); +char AnimCodeByWeapon(TGameObj* weapon); +void DisplayConsoleMessage(const char* msg); +const char* _stdcall GetMessageStr(DWORD fileAddr, DWORD messageId); +int __stdcall ItemGetType(TGameObj* item); \ No newline at end of file diff --git a/sfall/FalloutStructs.h b/sfall/FalloutStructs.h new file mode 100644 index 00000000..2bfd4acb --- /dev/null +++ b/sfall/FalloutStructs.h @@ -0,0 +1,157 @@ +/* +* sfall +* Copyright (C) 2008-2015 The sfall team +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#pragma once + + +/******************************************************************************/ +/* FALLOUT2.EXE structs, function offsets and wrappers should be placed here */ +/******************************************************************************/ + + +struct TGameObj; + +/* 26 */ +#pragma pack(push, 1) +struct TInvenRec +{ + TGameObj *object; + int count; +}; +#pragma pack(pop) + +/* 8 */ +#pragma pack(push, 1) +struct TGameObj +{ + int ID; + int tile; + char gap_8[2]; + char field_A; + char gap_B[17]; + int rotation; + int artFID; + char gap_24[4]; + int elevation; + int invenCount; + int field_30; + TInvenRec *invenTablePtr; + char gap_38[4]; + int itemCharges; + int movePoints; + char gap_44[16]; + int lastTarget; + char gap_58[12]; + int pid; + char gap_68[16]; + int scriptID; + char gap_7C[4]; + int script_index; + char gap_84[7]; + char field_0; +}; +#pragma pack(pop) + +/* 9 */ +#pragma pack(push, 1) +struct TComputeAttack +{ + TGameObj *attacker; + char gap_4[4]; + TGameObj *weapon; + char gap_C[4]; + int damageAttacker; + int flagsAttacker; + int rounds; + char gap_1C[4]; + TGameObj *target; + int targetTile; + int bodyPart; + int damageTarget; + int flagsTarget; + int knockbackValue; +}; +#pragma pack(pop) + + +/* 22 */ +#pragma pack(push, 1) +struct TScript +{ + int script_id; + char gap_4[4]; + int elevation_and_tile; + int spatial_radius; + char gap_10[4]; + int script_index; + int program_ptr; + int self_obj_id; + char gap_20[8]; + int scr_return; + char gap_2C[4]; + int fixed_param; + TGameObj *self_obj; + TGameObj *source_obj; + TGameObj *target_obj; + int script_overrides; + char field_44; + char gap_45[15]; + int procedure_table[28]; +}; +#pragma pack(pop) + +enum ScriptTypes +{ + SCRIPT_SYSTEM = 0, + SCRIPT_SPATIAL = 1, + SCRIPT_TIME = 2, + SCRIPT_ITEM = 3, + SCRIPT_CRITTER = 4, +}; + +/* 24 */ +enum ObjectTypes +{ + OBJ_TYPE_ITEM = 0, + OBJ_TYPE_CRITTER = 1, + OBJ_TYPE_SCENERY = 2, + OBJ_TYPE_WALL = 3, + OBJ_TYPE_TILE = 4, + OBJ_TYPE_MISC = 5, + OBJ_TYPE_SPATIAL = 6, +}; + +/* 25 */ +#pragma pack(push, 1) +struct TProgram +{ + char gap_0[4]; + int *codeStackPtr; + char gap_8[8]; + int *codePtr; + int field_14; + char gap_18[4]; + int *dStackPtr; + int *aStackPtr; + int *dStackOffs; + int *aStackOffs; + char gap_2C[4]; + int *stringRefPtr; + char gap_34[4]; + int *procTablePtr; +}; +#pragma pack(pop) \ No newline at end of file diff --git a/sfall/FileSystem.cpp b/sfall/FileSystem.cpp new file mode 100644 index 00000000..63ccab0f --- /dev/null +++ b/sfall/FileSystem.cpp @@ -0,0 +1,708 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "FileSystem.h" +#include "vector9x.cpp" + +extern void GetSavePath(char* buf, int type); + +struct fsFile { + char* data; + DWORD length; + char name[128]; + DWORD wpos; +}; + +vector files; + +static DWORD loadedtiles=0; +static DWORD retval; +static bool UsingFileSystem=false; + +struct openFile { + DWORD pos; + fsFile* file; + + openFile(fsFile* pFile) { + pos=0; + file=pFile; + } +}; + +struct sFile { + DWORD type; + openFile* file; +}; + +static const DWORD ptr_xfclose=0x4DED6C; +static void _stdcall xfclose(sFile* file) { + delete file->file; + delete file; +} +static void __declspec(naked) asm_xfclose(sFile* file) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push eax; + call xfclose; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfclose; + } +} + +static const DWORD ptr_xfopen=0x4DEE2C; +static sFile* _stdcall xfopen(const char* path, const char* mode) { + for(DWORD i=0;itype=3; + file->file=new openFile(&files[i]); + return file; + } + } + return 0; +} +static __declspec(naked) sFile* asm_xfopen(const char* path, const char* mode) { + __asm { + pushad; + push edx; + push eax; + call xfopen; + test eax, eax; + jz end; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfopen; + } +} + +//db_fprintf calls xvfprintf, not xfprintf +static const DWORD ptr_xvfprintf=0x4DF1AC; +static int _stdcall xvfprintf() { + return -1; +} +static __declspec(naked) int asm_xvfprintf(sFile* file, const char* format, void* vaargs) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + call xvfprintf; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xvfprintf; + } +} +static const DWORD ptr_xfgetc=0x4DF22C; +static int _stdcall xfgetc(sFile* file) { + if(file->file->pos>=file->file->file->length) return -1; + return file->file->file->data[file->file->pos++]; +} +static __declspec(naked) int asm_xfgetc(sFile* file) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push eax; + call xfgetc; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfgetc; + } +} + +static const DWORD ptr_xfgets=0x4DF280; +static char* _stdcall xfgets(char* buf, int max_count, sFile* file) { + if(file->file->pos>=file->file->file->length) return 0; + for(int i=0;ifile->pos==0) return -1; + if(file->file->file->data[file->file->pos-1]!=c) return -1; + file->file->pos--; + return c; +} +static __declspec(naked) int asm_xfungetc(int c, sFile* file) { + __asm { + pushad; + mov ecx, [edx]; + cmp cl, 3; + jnz end; + push edx; + push eax; + call xfungetc; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfungetc; + } +} +static const DWORD ptr_xfread=0x4DF44C; +static int _stdcall xfread(void* buf, int elsize, int count, sFile* file) { + for(int i=0;ifile->pos+elsize>=file->file->file->length) return i; + memcpy(buf, &file->file->file->data[file->file->pos], elsize); + file->file->pos+=elsize; + } + return count; +} +static __declspec(naked) int asm_xfread(void* buf, int elsize, int count, sFile* file) { + __asm { + pushad; + mov edi, [ecx]; + cmp di, 3; + jnz end; + push ecx; + push ebx; + push edx; + push eax; + call xfread; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfread; + } +} +static const DWORD ptr_xfwrite=0x4DF4E8; +static int _stdcall xfwrite(const void* buf, int elsize, int count, sFile* file) { + return 0; +} +static __declspec(naked) int asm_xfwrite(const void* buf, int elsize, int count, sFile* file) { + __asm { + pushad; + mov edi, [ecx]; + cmp di, 3; + jnz end; + push ecx; + push ebx; + push edx; + push eax; + call xfwrite; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfwrite; + } +} +static const DWORD ptr_xfseek=0x4DF5D8; +static int _stdcall xfseek(sFile* file, long pos, int origin) { + switch(origin) { + case 1: + file->file->pos+=pos; + break; + case 2: + file->file->pos=file->file->file->length+pos; + break; + case 0: + file->file->pos=pos; + break; + } + return 0; +} +static __declspec(naked) int asm_xfseek(sFile* file, long pos, int origin) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push ebx; + push edx; + push eax; + call xfseek; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfseek; + } +} +static const DWORD ptr_xftell=0x4DF690; +static long _stdcall xftell(sFile* file) { + return file->file->pos; +} +static __declspec(naked) long asm_xftell(sFile* file) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push eax; + call xftell; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xftell; + } +} +static const DWORD ptr_xfrewind=0x4DF6E4; +static void _stdcall xfrewind(sFile* file) { + file->file->pos=0; +} +static __declspec(naked) void asm_xfrewind(sFile* file) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push eax; + call xfrewind; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfrewind; + } +} +static const DWORD ptr_xfeof=0x4DF780; +static int _stdcall xfeof(sFile* file) { + if(file->file->pos>=file->file->file->length) return 1; + else return 0; +} +static __declspec(naked) int asm_xfeof(sFile* file) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push eax; + call xfeof; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfeof; + } +} +static const DWORD ptr_xfilelength=0x4DF828; +static int _stdcall xfilelength(sFile* file) { + return file->file->file->length; +} +static __declspec(naked) int asm_xfilelength(sFile* file) { + __asm { + pushad; + mov ecx, [eax]; + cmp cl, 3; + jnz end; + push eax; + call xfilelength; + mov retval, eax; + popad; + mov eax, retval; + retn; +end: + popad; + jmp ptr_xfilelength; + } +} + +void FileSystemReset() { + if(!UsingFileSystem) return; + for(DWORD i=loadedtiles;i=loadedtiles;i--) files.remove_at(i); + } +} +void FileSystemSave(HANDLE h) { + DWORD count=0, unused; + if(!UsingFileSystem) { + WriteFile(h, &count, 4, &unused, 0); + return; + } + for(DWORD i=0;idata=fdata; + strcpy_s(fsfile->name, path); + fsfile->length=fsize; + fsfile->wpos=0; + return result; +} +DWORD _stdcall FSfind(const char* path) { + if(!*path) return -1; + for(DWORD i=0;i=files.size()||!files[id].data) return; + if(files[id].wpos+1>files[id].length) return; + files[id].data[files[id].wpos++]=(char)data; +} +void _stdcall FSwrite_short(DWORD id, int data) { + if(id>=files.size()||!files[id].data) return; + if(files[id].wpos+2>files[id].length) return; + char data2[2]; + memcpy(data2, &data, 2); + char c=data2[0]; + data2[0]=data2[1]; + data2[1]=c; + for(int i=0;i<2;i++) files[id].data[files[id].wpos++]=data2[i]; +} +void _stdcall FSwrite_int(DWORD id, int data) { + if(id>=files.size()||!files[id].data) return; + if(files[id].wpos+4>files[id].length) return; + char data2[4]; + memcpy(data2, &data, 4); + char c=data2[1]; + data2[1]=data2[2]; + data2[2]=c; + c=data2[0]; + data2[0]=data2[3]; + data2[3]=c; + for(int i=0;i<4;i++) files[id].data[files[id].wpos++]=data2[i]; +} +void _stdcall FSwrite_string(DWORD id, const char* data) { + if(id>=files.size()||!files[id].data) return; + if(files[id].wpos+strlen(data)+1>files[id].length) return; + memcpy(&files[id].data[files[id].wpos], data, strlen(data)+1); + files[id].wpos+=strlen(data)+1; +} +void _stdcall FSwrite_bstring(DWORD id, const char* data) { + if(id>=files.size()||!files[id].data) return; + if(files[id].wpos+strlen(data)>files[id].length) return; + memcpy(&files[id].data[files[id].wpos], data, strlen(data)); + files[id].wpos+=strlen(data); +} +int _stdcall FSread_byte(DWORD id) { + if(id>=files.size()||!files[id].data) return 0; + if(files[id].wpos+1>files[id].length) return 0; + return files[id].data[files[id].wpos++]; +} +int _stdcall FSread_short(DWORD id) { + if(id>=files.size()||!files[id].data) return 0; + if(files[id].wpos+2>files[id].length) return 0; + char data[2]; + data[1]=files[id].data[files[id].wpos++]; + data[0]=files[id].data[files[id].wpos++]; + short result; + memcpy(&result, data, 2); + return result; +} +int _stdcall FSread_int(DWORD id) { + if(id>=files.size()||!files[id].data) return 0; + if(files[id].wpos+4>files[id].length) return 0; + char data[4]; + data[3]=files[id].data[files[id].wpos++]; + data[2]=files[id].data[files[id].wpos++]; + data[1]=files[id].data[files[id].wpos++]; + data[0]=files[id].data[files[id].wpos++]; + int result; + memcpy(&result, data, 4); + return result; +} +void _stdcall FSdelete(DWORD id) { + if(id>=files.size()||!files[id].data) return; + files[id].length=0; + files[id].wpos=0; + files[id].name[0]=0; + delete[] files[id].data; + files[id].data=0; +} +DWORD _stdcall FSsize(DWORD id) { + if(id>=files.size()||!files[id].data) return 0; + return files[id].length; +} +DWORD _stdcall FSpos(DWORD id) { + if(id>=files.size()||!files[id].data) return 0; + return files[id].wpos; +} +void _stdcall FSseek(DWORD id, DWORD pos) { + if(id>=files.size()||!files[id].data) return; + if(pos>files[id].length) return; + files[id].wpos=pos; +} +void _stdcall FSresize(DWORD id, DWORD size) { + if(id>=files.size()||!files[id].data) return; + char* buf=files[id].data; + files[id].data=new char[size]; + CopyMemory(files[id].data, buf, min(files[id].length, size)); + files[id].length=size; + files[id].wpos=0; + delete[] buf; +} diff --git a/sfall/FileSystem.h b/sfall/FileSystem.h new file mode 100644 index 00000000..47f8ae1c --- /dev/null +++ b/sfall/FileSystem.h @@ -0,0 +1,42 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void FileSystemInit(); +void FileSystemReset(); +void FileSystemSave(HANDLE h); + +DWORD _stdcall FScreate(const char* path, int size); +DWORD _stdcall FScreateFromData(const char* path, void* data, int size); +DWORD _stdcall FScopy(const char* path, const char* source); +DWORD _stdcall FSfind(const char* path); +void _stdcall FSwrite_byte(DWORD id, int data); +void _stdcall FSwrite_short(DWORD id, int data); +void _stdcall FSwrite_int(DWORD id, int data); +//void _stdcall fs_write_float(DWORD id, int data); +void _stdcall FSwrite_string(DWORD id, const char* data); +void _stdcall FSwrite_bstring(DWORD id, const char* data); +int _stdcall FSread_byte(DWORD id); +int _stdcall FSread_short(DWORD id); +int _stdcall FSread_int(DWORD id); + void _stdcall FSdelete(DWORD id); +DWORD _stdcall FSsize(DWORD id); +DWORD _stdcall FSpos(DWORD id); +void _stdcall FSseek(DWORD id, DWORD pos); +void _stdcall FSresize(DWORD id, DWORD size); diff --git a/sfall/Graphics.h b/sfall/Graphics.h new file mode 100644 index 00000000..eba4094f --- /dev/null +++ b/sfall/Graphics.h @@ -0,0 +1,42 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +extern DWORD GraphicsMode; +void graphics_OnGameLoad(); + +int _stdcall GetShaderVersion(); +int _stdcall LoadShader(const char*); +void _stdcall ActivateShader(DWORD); +void _stdcall DeactivateShader(DWORD); +void _stdcall FreeShader(DWORD); +void _stdcall SetShaderMode(DWORD d, DWORD mode); + +void _stdcall SetShaderInt(DWORD d, const char* param, int value); +void _stdcall SetShaderFloat(DWORD d, const char* param, float value); +void _stdcall SetShaderVector(DWORD d, const char* param, float f1, float f2, float f3, float f4); + +int _stdcall GetShaderTexture(DWORD d, DWORD id); +void _stdcall SetShaderTexture(DWORD d, const char* param, DWORD value); + +void RefreshGraphics(); +void GetFalloutWindowInfo(DWORD* width, DWORD* height, HWND* window); + +void SetMovieTexture(void* tex); +void _stdcall PlayMovieFrame(); diff --git a/sfall/HeroAppearance.cpp b/sfall/HeroAppearance.cpp new file mode 100644 index 00000000..1637dcc2 --- /dev/null +++ b/sfall/HeroAppearance.cpp @@ -0,0 +1,2814 @@ +/* + * sfall + * Copyright (C) 2009, 2010 Mash (Matt Wells, mashw at bigpond dot net dot au) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include "HeroAppearance.h" +#include "ScriptExtender.h" + + + bool AppModEnabled=false;//check if Appearance mod enabled for script fuctions + + + //char scrn surfaces + BYTE *NewButt01Surface=NULL; + BYTE *CharScrnBackSurface=NULL; + + //char scrn critter rotation vars + DWORD CharRotTick=0; + DWORD CharRotOri=0; + + + int CurrentRaceVal=0, CurrentStyleVal=0;//holds Appearance values to restore after global reset in NewGame2 fuction in LoadGameHooks.cpp + + DWORD critterListSize=0, critterArraySize=0;//Critter art list size + + + //fallout2 path node structure +struct sPath { + char* path; + void* pDat; + DWORD isDat; + sPath* next; +}; + +sPath **TempPathPtr=(sPath**)0x6B24D0; +sPath *HeroPathPtr=NULL; +sPath *RacePathPtr=NULL; + + +//for word wrapping +typedef struct LINENode { + DWORD offset; + LINENode *next; + + LINENode() { + next=NULL; + offset=0; + } +}LINENode; + + +//for holding a message +typedef struct MSGNode { + DWORD ref; + DWORD a; + char *msg1;//unused + char *msg2; + + MSGNode() { + ref=0; + a=0; + msg1=NULL; + msg2=NULL; + } +}MSGNode; + + +//for holding msg array +typedef struct MSGList { + long numMsgs; + void *MsgNodes; + + MSGList() { + MsgNodes=NULL; + numMsgs=0; + } +}MSGList; + + + +//structures for holding frms loaded with fallout2 functions +#pragma pack(2) +typedef class FRMframe { + public: + WORD width; + WORD height; + DWORD size; + WORD x; + WORD y; +} FRMframe; + +typedef class FRMhead { + public: + DWORD version;//version num + WORD FPS;//frames per sec + WORD actionFrame; + WORD numFrames;//number of frames per direction + WORD xCentreShift[6];//offset from frm centre +=right -=left + WORD yCentreShift[6];//offset from frm centre +=down -=up + DWORD oriOffset[6];//frame area offset for diff orientations + DWORD frameAreaSize; +} FRMhead; +#pragma pack() + + +//structures for loading unlisted frms +typedef class UNLSTDframe { + public: + WORD width; + WORD height; + DWORD size; + WORD x; + WORD y; + BYTE *indexBuff; + UNLSTDframe() { + width=0; + height=0; + size=0; + x=0; + y=0; + indexBuff=NULL; + } + ~UNLSTDframe() { + if(indexBuff!=NULL) + delete[] indexBuff; + } +} UNLSTDframe; + +typedef class UNLSTDfrm { + public: + DWORD version; + WORD FPS; + WORD actionFrame; + WORD numFrames; + WORD xCentreShift[6]; + WORD yCentreShift[6]; + DWORD oriOffset[6]; + DWORD frameAreaSize; + UNLSTDframe *frames; + UNLSTDfrm() { + version=0; + FPS=0; + actionFrame=0; + numFrames=0; + for(int i=0; i<6; i++){ + xCentreShift[i]=0; + yCentreShift[i]=0; + oriOffset[i]=0; + } + frameAreaSize=0; + frames=NULL; + } + ~UNLSTDfrm() { + if(frames!=NULL) + delete[] frames; + } +} UNLSTDfrm; + + + + +/* +WORD ByteSwap16 (WORD num) { + return (((num >> 8)) | (num << 8)); +} + +DWORD ByteSwap32 (DWORD num) { + return (((num&0x000000FF)<<24)+((num&0x0000FF00)<<8)+ + ((num&0x00FF0000)>>8)+((num&0xFF000000)>>24)); +} +*/ + +DWORD funcTemp=0;//temp var for holding fallout2 function addresses + +/////////////////////////////////////////////////////////////////FILE FUNCTIONS//////////////////////////////////////////////////////////////////////// +//-------------------------------------------------- +void* FOpenFile(char *FileName, char *flags) { + void *retVal; + __asm { + mov edx, flags + mov eax, FileName + mov funcTemp, 0x4DEE2C + call funcTemp + mov retVal, eax + } + return retVal; +} + +//-------------------------------------------------- +int FCloseFile(void *FileStream) { + int retVal; + __asm { + + mov eax, FileStream + mov funcTemp, 0x4C5EB4 + call funcTemp + mov retVal, eax + } + return retVal; +} + + + +//-------------------------------------------------- +int Ffseek(void *FileStream, long fOffset, int origin) { + int retVal; + __asm { + mov ebx, origin + mov edx, fOffset + mov eax, FileStream + mov funcTemp, 0x4DF5D8 + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//-------------------------------------------------- +int FReadByte(void *FileStream, BYTE *toMem) { + int retVal; + __asm { + mov edx, toMem + mov eax, FileStream + mov funcTemp, 0x4C60E0 + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//-------------------------------------------------- +int FReadWord(void *FileStream, WORD *toMem) { + int retVal; + __asm { + mov edx, toMem + mov eax, FileStream + mov funcTemp, 0x4C60F4 + call funcTemp + mov retVal, eax + } + return retVal; +} + +//-------------------------------------------------- +int FReadDword(void *FileStream, DWORD *toMem) { + int retVal; + __asm { + mov edx, toMem + mov eax, FileStream + mov funcTemp, 0x4C614C + call funcTemp + mov retVal, eax + } + return retVal; +} + +//-------------------------------------------------- +int FReadWordArray(void *FileStream, WORD *toMem, DWORD NumElements) { + int retVal; + __asm { + mov ebx, NumElements + mov edx, toMem + mov eax, FileStream + mov funcTemp, 0x4C6330 + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//-------------------------------------------------- +int FReadDwordArray(void *FileStream, DWORD *toMem, DWORD NumElements) { + int retVal; + __asm { + mov ebx, NumElements + mov edx, toMem + mov eax, FileStream + mov funcTemp, 0x4C63BC + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//-------------------------------------------------- +int FReadString(void *FileStream, char *toMem, DWORD charLength, DWORD NumStrings) { + int retVal; + __asm { + mov ecx, FileStream + mov ebx, NumStrings + mov edx, charLength + mov eax, toMem + mov funcTemp, 0x4C5FFC + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//-------------------------------------------------- +int FWriteByte(void *FileStream, BYTE bVal) { + int retVal; + __asm { + xor edx, edx + mov dl, bVal + mov eax, FileStream + mov funcTemp, 0x4C61AC + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//-------------------------------------------------- +int FWriteDword(void *FileStream, DWORD bVal) { + int retVal; + __asm { + mov edx, bVal + mov eax, FileStream + mov funcTemp, 0x4C6214 + call funcTemp + mov retVal, eax + } + return retVal; +} + + +/////////////////////////////////////////////////////////////////MESSAGE FUNCTIONS//////////////////////////////////////////////////////////////////////// + +//-------------------------------------------------- +int LoadMsgList(MSGList *MsgList, char *MsgFilePath) { + int retVal; + __asm { + mov edx, MsgFilePath + mov eax, MsgList + mov funcTemp, 0x484AA4 + call funcTemp + mov retVal, eax + } + return retVal; +} + + +//----------------------------------------- +int DestroyMsgList(MSGList *MsgList) { + int retVal; + __asm { + mov eax, MsgList + mov funcTemp, 0x484964 + call funcTemp + mov retVal, eax + } + return retVal; +} + +/* +//----------------------------------------------------------- +bool GetMsg(MSGList *MsgList, MSGNode *MsgNode, DWORD msgRef) { + bool retVal=FALSE; + MsgNode->ref=msgRef; + + __asm { + mov edx, MsgNode + mov eax, MsgList + mov funcTemp, 0x484C30 + call funcTemp + cmp eax, 1 + jne EndFunc + mov retVal, 1 + EndFunc: + } +return retVal; +} +*/ + + +//---------------------------------------------------- +MSGNode *GetMsgNode(MSGList *MsgList, DWORD msgRef) { + + if(MsgList==NULL)return NULL; + if(MsgList->numMsgs<=0)return NULL; + + MSGNode *MsgNode=(MSGNode*)MsgList->MsgNodes; + + long last=MsgList->numMsgs-1; + long first=0; + long mid; + + //Use Binary Search to find msg + while (first <= last) { + mid = (first + last)/2; + if(msgRef > MsgNode[mid].ref) + first = mid + 1; + else if(msgRef < MsgNode[mid].ref) + last = mid - 1; + else + return &MsgNode[mid]; + } + + return NULL; +} + + +//-------------------------------------------------------- +char* GetMsg(MSGList *MsgList, DWORD msgRef, int msgNum) { + MSGNode *MsgNode = GetMsgNode(MsgList, msgRef); + if(MsgNode) { + if(msgNum==2) + return MsgNode->msg2; + else if(msgNum==1) + return MsgNode->msg1; + } + return NULL; +} + + + +/////////////////////////////////////////////////////////////////MOUSE FUNCTIONS//////////////////////////////////////////////////////////////////////// +//----------------------------------------------------- +//get current mouse pic ref +int GetMousePic() { + return *(DWORD*)0x518C0C; +} + +//----------------------------------------------------- +//set mouse pic +int SetMousePic(int picNum) { + + __asm { + mov eax, picNum + mov funcTemp, 0x44C840 + call funcTemp + mov picNum, eax + } +return picNum;//0 = success, -1 = fail +} + + +//-------------------------------------------------- +void GetMousePos(int *x_out, int *y_out) { + + __asm { + push esi + mov edx, y_out + mov eax, x_out + mov funcTemp, 0x4CA9DC + call funcTemp + pop esi + } +} + +//----------------------------------------------------- +void ShowMouse() { + + __asm { + mov funcTemp, 0x4CA34C + call funcTemp + } + +} + + +//----------------------------------------------------- +void HideMouse() { + + __asm { + mov funcTemp, 0x4CA534 + call funcTemp + } +} + + +//------------------------------------------------------- +//returns 0 if mouse is hidden +int IsMouseHidden() { + return *(DWORD*)0x6AC790; +} + + + +/////////////////////////////////////////////////////////////////FRM FUNCTIONS//////////////////////////////////////////////////////////////////////// + +//-------------------------------------------------- +DWORD LoadFrm(DWORD LstRef, DWORD LstNum) { + DWORD FrmID; + + __asm { + push 0 + xor ecx,ecx + xor ebx,ebx + mov edx,LstNum + mov eax,LstRef + mov funcTemp, 0x419C88 + call funcTemp + mov FrmID, eax + } + return FrmID; +} + + +//--------------------------------------------- +void UnloadFrm(DWORD FrmObj) { + + __asm { + mov eax, FrmObj + mov funcTemp, 0x419260 + call funcTemp + } +} + +//-------------------------------------------------------- +BYTE* GetFrmSurface(DWORD FrmID, DWORD FrameNum, DWORD Ori, DWORD *FrmObj_out) { + BYTE *Surface; + + __asm { + mov ecx, FrmObj_out//0x518F4C + mov ebx, Ori + mov edx, FrameNum + mov eax, FrmID + mov funcTemp, 0x419188 + call funcTemp + mov Surface,eax + } + return Surface; +} + + +//-------------------------------------------------------- +BYTE* GetFrmSurface2(DWORD FrmID, DWORD *FrmObj_out, DWORD *frmWidth_out, DWORD *frmHeight_out) { + BYTE *Surface; + + __asm { + mov ecx, frmHeight_out + mov ebx, frmWidth_out + mov edx, FrmObj_out//0x518F4C + mov eax, FrmID + mov funcTemp, 0x4191CC + call funcTemp + mov Surface,eax + } + return Surface; +} + +//-------------------------------------------------------- +FRMhead* GetFrm(DWORD FrmID, DWORD *FrmObj_out) { + FRMhead* Frm; + + __asm { + mov edx, FrmObj_out + mov eax, FrmID + mov funcTemp, 0x419160 + call funcTemp + mov Frm, eax + } + return Frm; +} + +//-------------------------------------------------------- +DWORD GetFrmFrameWidth(FRMhead* Frm, DWORD FrameNum, DWORD Ori) { + DWORD Width; + + __asm { + mov ebx, Ori//0-5 + mov edx, FrameNum + mov eax, Frm + mov funcTemp, 0x4197A0 + call funcTemp + mov Width,eax + } + return Width; +} + + +//-------------------------------------------------------- +DWORD GetFrmFrameHeight(FRMhead* Frm, DWORD FrameNum, DWORD Ori) { + DWORD Height; + + __asm { + mov ebx, Ori//0-5 + mov edx, FrameNum + mov eax, Frm + mov funcTemp, 0x4197B8 + call funcTemp + mov Height,eax + } + return Height; +} + + +//-------------------------------------------------------- +BYTE* GetFrmFrameSurface(FRMhead* Frm, DWORD FrameNum, DWORD Ori) { + BYTE *Surface; + + __asm { + mov ebx, Ori//0-5 + mov edx, FrameNum + mov eax, Frm + mov funcTemp, 0x419870 + call funcTemp + mov Surface,eax + } + return Surface; +} + + + +/////////////////////////////////////////////////////////////////UNLISTED FRM FUNCTIONS//////////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------------- +bool LoadFrmHeader(UNLSTDfrm *frmHeader, void*frmStream) { + + if(FReadDword(frmStream, &frmHeader->version)==-1)return 0; + else if(FReadWord(frmStream, &frmHeader->FPS)==-1)return 0; + else if(FReadWord(frmStream, &frmHeader->actionFrame)==-1)return 0; + else if(FReadWord(frmStream, &frmHeader->numFrames)==-1)return 0; + + else if(FReadWordArray(frmStream, frmHeader->xCentreShift, 6)==-1)return 0; + else if(FReadWordArray(frmStream, frmHeader->yCentreShift, 6)==-1)return 0; + else if(FReadDwordArray(frmStream, frmHeader->oriOffset, 6)==-1)return 0; + else if(FReadDword(frmStream, &frmHeader->frameAreaSize)==-1)return 0; + + return 1; +} + +//------------------------------------------------------------ +bool LoadFrmFrame(UNLSTDframe *frame, void *frmStream) { + + //FRMframe *frameHeader = (FRMframe*)frameMEM; + //BYTE* frameBuff = frame + sizeof(FRMframe); + + if(FReadWord(frmStream, &frame->width)==-1)return 0; + else if(FReadWord(frmStream, &frame->height)==-1)return 0; + else if(FReadDword(frmStream, &frame->size)==-1)return 0; + else if(FReadWord(frmStream, &frame->x)==-1)return 0; + else if(FReadWord(frmStream, &frame->y)==-1)return 0; + frame->indexBuff= new BYTE[frame->size]; + if(FReadString(frmStream, (char*)frame->indexBuff, frame->size, 1)!=1)return 0; + + return 1; +} + + +//------------------------------------------------------------------- +UNLSTDfrm *LoadUnlistedFrm(char *FrmName, unsigned int folderRef) { + + if(folderRef>10) return NULL; + + char*artfolder = (char*)(0x51073C+folderRef*32);//address of art type name + char FrmPath[MAX_PATH]; + + sprintf_s(FrmPath, MAX_PATH, "art\\%s\\%s\0", artfolder, FrmName); + + + UNLSTDfrm *frm = new UNLSTDfrm; + + void *frmStream = FOpenFile(FrmPath, "rb"); + + + if(frmStream){ + if(!LoadFrmHeader(frm, frmStream)) { + FCloseFile(frmStream); + delete frm; + return NULL; + } + + DWORD oriOffset_1st=frm->oriOffset[0]; + DWORD oriOffset_new=0; + frm->frames = new UNLSTDframe[6*frm->numFrames]; + for(int ori=0; ori<6; ori++) { + if(ori==0 || frm->oriOffset[ori]!=oriOffset_1st) { + frm->oriOffset[ori]=oriOffset_new; + for(int fNum=0; fNumnumFrames; fNum++) { + if(!LoadFrmFrame(&frm->frames[oriOffset_new+fNum], frmStream)) { + FCloseFile(frmStream); + delete frm; + return NULL; + } + } + oriOffset_new+=frm->numFrames; + } + else frm->oriOffset[ori]=0; + } + + FCloseFile(frmStream); + } + else { + delete frm; + return NULL; + } + return frm; +} + + + +/////////////////////////////////////////////////////////////////WINDOW FUNCTIONS//////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------------------------- +int CreateWin(DWORD x, DWORD y, DWORD width, DWORD height, DWORD BGColourIndex, DWORD flags) { + int WinRef; + + __asm { + push flags + push BGColourIndex + mov ecx,height + mov ebx,width + mov edx, y + mov eax, x + mov funcTemp, 0x4D6238 + call funcTemp + mov WinRef, eax + } + return WinRef; +} + + +//---------------------------------------------------------------------------------- +void DestroyWin(int WinRef) { + + __asm { + mov eax, WinRef + mov funcTemp, 0x4D6468 + call funcTemp + } +} + + +//-------------------------------------------------- +WINinfo *GetWinStruct(int WinRef) { + WINinfo *winStruct; + + __asm { + push edx + mov eax, WinRef + mov funcTemp, 0x4D7888 + call funcTemp + mov winStruct, eax + pop edx + } + return winStruct; +} + + + +//--------------------------------- +BYTE* GetWinSurface(int WinRef) { + BYTE *surface; + + __asm { + mov eax, WinRef + mov funcTemp, 0x4D78B0 + call funcTemp + mov surface, eax + } + return surface; +} + +//---------------------------------------------------------------------------------- +void ShowWin(int WinRef) { + + __asm { + mov eax, WinRef + mov funcTemp, 0x4D6DAC + call funcTemp + } +} +/* +//---------------------------------------------------------------------------------- +void HideWin(int WinRef) { + + __asm { + mov eax, WinRef + mov funcTemp, 0x4D6E64 + call funcTemp + } +} +*/ +//----------------------------------------------------------------------------------------------------------------------- +void RedrawWin(int WinRef) { + + __asm { + mov eax, WinRef + mov funcTemp, 0x4D6F5C + call funcTemp + } +} + + +/////////////////////////////////////////////////////////////////BUTTON FUNCTIONS//////////////////////////////////////////////////////////////////////// +//--------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +int CreateButton(int WinRef, DWORD Xpos, DWORD Ypos, DWORD Width, DWORD Height, DWORD HovOn, DWORD HovOff, + DWORD ButtDown, DWORD ButtUp, BYTE *PicUp, BYTE *PicDown, DWORD ButType) { + int ret_val; + + __asm { + push ButType//button type: 0x10 = move window pos, 0x20 or 0x0 = regular click, 0x23 = toggle click + push 0x0//? always 0 + push PicDown//0//button down pic index + push PicUp//0//button up pic index + push ButtUp + push ButtDown + push HovOff + push HovOn + push Height + mov ecx, Width + mov edx, Xpos + mov ebx, Ypos + mov eax, WinRef + mov funcTemp, 0x4D8260 + call funcTemp + mov ret_val, eax + } + return ret_val; +} + +//----------------------------- +int check_buttons(void) { +int key_code; + + __asm { + mov funcTemp, 0x4C8B78 + call funcTemp + mov key_code, eax + } + return key_code; +} + + +/////////////////////////////////////////////////////////////////TEXT FUNCTIONS//////////////////////////////////////////////////////////////////////// +//------------------------- +void SetFont(int ref) { + + __asm { + mov eax, ref + mov funcTemp, 0x4D58DC + call funcTemp + } +} + +//----------------------- +int GetFont(void) { +return *(DWORD*)0x51E3B0; +} + + +//--------------------------------------------------------- +//print text to surface +void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { + DWORD posOffset = Ypos*ToWidth+Xpos; + __asm { + xor eax, eax + MOV AL,ColourIndex + push eax + mov edx, DisplayText + mov ebx, TxtWidth + mov ecx, ToWidth + mov eax, ToSurface + add eax, posOffset + call dword ptr ds:[0x51E3B8] + } +} + +//--------------------------------------------------------- +//gets the height of the currently selected font +DWORD GetTextHeight() { + DWORD TxtHeight; + __asm { + call dword ptr ds:[0x51E3BC]//get text height + mov TxtHeight, eax + } + return TxtHeight; +} + +//--------------------------------------------------------- +//gets the length of a string using the currently selected font +DWORD GetTextWidth(char *TextMsg) { + DWORD TxtWidth; + __asm { + mov eax, TextMsg + call dword ptr ds:[0x51E3C0]//get text width + mov TxtWidth, eax + } + return TxtWidth; +} + +//--------------------------------------------------------- +//get width of Char for current font +DWORD GetCharWidth(char CharVal) { + DWORD charWidth; + __asm { + mov al, CharVal + call dword ptr ds:[0x51E3C4] + mov charWidth, eax + } +return charWidth; +} + +//--------------------------------------------------------- +//get maximum string length for current font - if all characters were maximum width +DWORD GetMaxTextWidth(char *TextMsg) { + DWORD msgWidth; + __asm { + mov eax, TextMsg + call dword ptr ds:[0x51E3C8] + mov msgWidth, eax + } +return msgWidth; +} + +//--------------------------------------------------------- +//get number of pixels between characters for current font +DWORD GetCharGapWidth() { + DWORD gapWidth; + __asm { + call dword ptr ds:[0x51E3CC] + mov gapWidth, eax + } +return gapWidth; +} + +//--------------------------------------------------------- +//get maximum character width for current font +DWORD GetMaxCharWidth() { + DWORD charWidth=0; + __asm { + call dword ptr ds:[0x51E3D4] + mov charWidth, eax + } +return charWidth; +} + + +/////////////////////////////////////////////////////////////////DAT FUNCTIONS//////////////////////////////////////////////////////////////////////// + + +//----------------------------------------- +void* LoadDat(char*FileName) { + void *dat=NULL; + __asm { + mov eax, FileName + mov funcTemp, 0x4E4F58 + call funcTemp + mov dat, eax + } + return dat; +} + + +//----------------------------------------- +void UnloadDat(void *dat) { + + __asm { + mov eax, dat + mov funcTemp, 0x4E5270 + call funcTemp + } +} + + +/////////////////////////////////////////////////////////////////OTHER FUNCTIONS//////////////////////////////////////////////////////////////////////// + +/* +void DrawLineX(int WinRef, DWORD XStartPos, DWORD XEndPos, DWORD Ypos, BYTE ColourIndex) { + + __asm { + xor eax, eax + mov al, ColourIndex + push eax + mov ebx, Ypos + push ebx + mov ecx, XEndPos + mov edx, XStartPos + mov eax, WinRef + + mov funcTemp, 0x4D6B24 + call funcTemp + } +} +*/ + + + +//--------------------------------------------------------- +void PlayAcm(char *AcmName) { + + __asm { + mov eax, AcmName + mov funcTemp, 0x4519A8 + call funcTemp + } +} + + + +//------------------------------ +void _stdcall RefreshArtCache(void) +{ + __asm { + + mov funcTemp, 0x41927C + call funcTemp + } +} + + + + + +// Check fallout paths for file---------------- +int CheckFile(char*FileName, DWORD *size_out) { +int retVal=0; + __asm { + mov edx, size_out + mov eax, FileName + mov funcTemp, 0x4C5D68 + call funcTemp + mov retVal, eax + } + return retVal; +} + + +/////////////////////////////////////////////////////////////////APP MOD FUNCTIONS//////////////////////////////////////////////////////////////////////// + + +//----------------------------------------- +char _stdcall GetSex(void) { + char sex; + __asm { + mov edx,0x22//sex stat ref + mov eax,dword ptr ds:[0x6610B8]//hero state structure + mov ebx, 0x4AEF48 + call ebx//get Player stat val + test eax, eax //male=0, female=1 + jne Female + mov sex, 'M' + jmp EndFunc + Female: + mov sex, 'F' + EndFunc: + } + return sex; +} + + + //---------------------------------------------------------------- +int _stdcall LoadHeroDat(unsigned int Race, unsigned int Style) { + + if(HeroPathPtr->pDat) {//unload previous Dats + UnloadDat(HeroPathPtr->pDat); + HeroPathPtr->pDat=NULL; + HeroPathPtr->isDat=0; + } + if(RacePathPtr->pDat) { + UnloadDat(RacePathPtr->pDat); + RacePathPtr->pDat=NULL; + RacePathPtr->isDat=0; + } + + sprintf_s(HeroPathPtr->path, 64, "Appearance\\h%cR%02dS%02d.dat\0", GetSex(), Race, Style); + if(GetFileAttributes(HeroPathPtr->path)!=INVALID_FILE_ATTRIBUTES) {//check if Dat exists for selected appearance + HeroPathPtr->pDat=LoadDat(HeroPathPtr->path); + HeroPathPtr->isDat=1; + } + else { + sprintf_s(HeroPathPtr->path, 64, "Appearance\\h%cR%02dS%02d\0", GetSex(), Race, Style); + if(GetFileAttributes(HeroPathPtr->path)==INVALID_FILE_ATTRIBUTES)//check if folder exists for selected appearance + return -1; + } + + TempPathPtr=&HeroPathPtr;//set path for selected appearance + HeroPathPtr->next = *(sPath**)0x6B24D0; + + if(Style!=0){ + sprintf_s(RacePathPtr->path, 64, "Appearance\\h%cR%02dS%02d.dat\0", GetSex(), Race, 0); + if(GetFileAttributes(RacePathPtr->path)!=INVALID_FILE_ATTRIBUTES) {//check if Dat exists for selected race base appearance + RacePathPtr->pDat=LoadDat(RacePathPtr->path); + RacePathPtr->isDat=1; + } + else + sprintf_s(RacePathPtr->path, 64, "Appearance\\h%cR%02dS%02d\0", GetSex(), Race, 0); + + if(GetFileAttributes(RacePathPtr->path)!=INVALID_FILE_ATTRIBUTES) {//check if folder/Dat exists for selected race base appearance + HeroPathPtr->next = RacePathPtr;//set path for selected race base appearance + RacePathPtr->next = *(sPath**)0x6B24D0; + } + + } + + return 0; +} + + +//--------------------------------------------------------- +//insert hero art path in front of main path structure when loading art +static void __declspec(naked) LoadNewHeroArt() { + + __asm { + cmp byte ptr ds:[esi], 'r' + je isReading + mov ecx, 0x6B24D0 + jmp setPath + isReading: + mov ecx, TempPathPtr + setPath: + mov ecx,dword ptr ds:[ecx] + ret + } + +} + +/* +//---------------------------------------------------------------- +int _stdcall CheckHeroFile(char* FileName) { + +char TempPath[64]; + // sprintf_s(TempPath, 64, "%s\\%s\0", cPath, FileName); + sprintf_s(TempPath, 64, "%s\\%s\0", HeroPathPtr->path, FileName); + + if(GetFileAttributes(TempPath)==INVALID_FILE_ATTRIBUTES) + return -1; + + + return 0; +} +*/ + +//--------------------------------------------------------- +static void __declspec(naked) CheckHeroExist() { + + __asm { + //pushad + cmp esi, critterArraySize//check if loading hero art + jle EndFunc + + sub esp, 0x4 + lea ebx, [esp] + push ebx + push 0x56C9E4//critter art file name address + //call CheckHeroFile//check if art file exists + call CheckFile + add esp, 0xC + cmp eax, -1 + jne EndFunc + + //pop eax//drop func ret address + add esp, 0x4 + //if file not found load regular critter art instead + sub esi, critterArraySize + //popad + mov eax, 0x4194E2 + jmp eax + EndFunc: + //popad + mov eax, 0x56C9E4 + ret + } + +} + + +//--------------------------------------------------------- +//adjust base hero art if num below hero art range +static void __declspec(naked) AdjustHeroBaseArt() { + + + __asm { + //cmp eax, critterListSize + // jg EndFunc + add eax, critterListSize +//EndFunc: + mov dword ptr ds:[0x5108A4],eax + ret + } + +} + + +//--------------------------------------------------------- +//adjust armor art if num below hero art range +static void __declspec(naked) AdjustHeroArmorArt() { + + __asm { + pop ebx//get ret addr + mov dword ptr ss:[esp], ebx//reinsert ret addr in old (push 0) + xor ebx, ebx + push 0 + mov esi, 0x419C88 + call esi//call load frm func + + mov dx, ax + and dh, 0xFF00//mask out current weapon flag + cmp edx, critterListSize//check if critter art in PC range + jg EndFunc + add eax, critterListSize//shift critter art index up into hero range + EndFunc: + //mov dword ptr ds:[0x59E95C],eax + ret + } + +} + + +//----------------------------------------- +void _stdcall SetHeroArt(int NewArtFlag) { + + __asm { + mov eax, dword ptr ds:[0x6610B8]//hero state struct + mov eax, dword ptr ds:[eax+0x20]//get hero FrmID + xor edx, edx + mov dx, ax + and dh, 0xFF00//mask out current weapon flag + cmp edx, critterListSize//check if critter LST index is in Hero range + jg IsHero + cmp NewArtFlag, 1 + jne EndFunc + add eax, critterListSize//shift index up into hero range + jmp SetArt + IsHero: + cmp NewArtFlag, 0 + jne EndFunc + sub eax, critterListSize//shift index down into normal critter range + SetArt: + mov ebx, 0//SomePtr + mov edx, eax + mov eax, dword ptr ds:[0x6610B8]//hero state struct + mov dword ptr ds:[eax+0x20],edx //copy new FrmID to hero state struct + //mov ecx, 0x48AA3C// set critter FrmID function + //call ecx + EndFunc: + } + +} + + +//---------------------------------------------- +//return hero art val to normal before saving +static void __declspec(naked) SavCritNumFix() { + + __asm { + + push eax + push edx + push 0 //set hero FrmID LST index to normal range before saving + call SetHeroArt + pop edx + pop eax + + push ebx + mov ebx, 0x48D59C//save current hero state structure fuction + call ebx + pop ebx + + push eax + push edx + push 1 //return hero FrmID LST index back to hero art range after saving hero state structure + call SetHeroArt + pop edx + pop eax + + ret + } + +} + + + +//--------------------------------------------------------- +static void __declspec(naked) DoubleArt() { + + __asm { + cmp dword ptr ss:[esp+0xCC], 0x510774 //check if loading critter lst. 0x510774= addr of critter list size val + jne EndFunc + shl edi, 1//double critter list size to add room for hero art + EndFunc: + mov eax, ecx + xor ebx, ebx + xor edx, edx + ret + } + +} + + +//------------------------------ +void FixCritList() { + //int size = (*(DWORD*)0x510774)/2;//critter list size after resize by DoubleArt func + critterListSize=(*(DWORD*)0x510774)/2; + critterArraySize=critterListSize*13; + + char *CritList = (*(char**)0x51076C);//critter list offset + char *HeroList = CritList+(critterArraySize);//set start of hero critter list after regular critter list + + memset( HeroList, '\0', critterArraySize); + + for(DWORD i=0; ifromWidth)subWidth=fromWidth-fromX; + //if(fromY+subHeight>fromHeight)subHeight=fromHeight-fromY; + + //if(toX+subWidth>toWidth)subWidth=toWidth-toX; + //if(toY+subHeight>toHeight)subHeight=toHeight-toY; + + fromBuff=fromBuff+fromY*fromWidth+fromX; + toBuff=toBuff+toY*toWidth+toX; + + for(long h=0; h0 || CurrentStyleVal>0) + SetAppearanceGlobals(CurrentRaceVal, CurrentStyleVal); +} + + + +//---------------------------------------------------------------------- +void _stdcall SetHeroStyle(int newStyleVal) { + + if(!AppModEnabled)return; + + if(newStyleVal==CurrentStyleVal)return; + + RefreshArtCache(); + + if(LoadHeroDat(CurrentRaceVal, newStyleVal)!=0) {//if new style cannot be set + if(CurrentRaceVal==0 && newStyleVal==0)CurrentStyleVal=0;//ignore error if appearance = default + else LoadHeroDat(CurrentRaceVal, CurrentStyleVal);//reload original style + } + else + CurrentStyleVal=newStyleVal; + + SetAppearanceGlobals(CurrentRaceVal, CurrentStyleVal); + DrawPC(); +} + + +//---------------------------------------------------------------------- +void _stdcall SetHeroRace(int newRaceVal) { + + if(!AppModEnabled)return; + + if(newRaceVal==CurrentRaceVal)return; + + RefreshArtCache(); + + if(LoadHeroDat(newRaceVal, 0)!=0) { //if new race fails with style at 0 + if(newRaceVal==0)CurrentRaceVal=0, CurrentStyleVal=0;//ignore if appearance = default + else LoadHeroDat(CurrentRaceVal, CurrentStyleVal);//reload original race & style + } + else + CurrentRaceVal=newRaceVal, CurrentStyleVal=0; + + SetAppearanceGlobals(CurrentRaceVal, CurrentStyleVal);//store new globals + DrawPC(); +} + + +//-------------------------------------------------------------------------------------- +bool CreateWordWrapList(char *TextMsg, DWORD WrapWidth, DWORD *lineNum, LINENode *StartLine) { + + *lineNum=1; + + if(GetMaxCharWidth()>=WrapWidth)return FALSE; + + if(GetTextWidth(TextMsg)next=new LINENode; + LINENode *NextLine=StartLine->next; + + DWORD lineWidth=0, wordWidth=0; + + char CurrentChar=NULL; + DWORD i=0; + + while(TextMsg[i]!=NULL) { + CurrentChar=TextMsg[i]; + + lineWidth=lineWidth+GetCharWidth(CurrentChar)+GapWidth; + wordWidth=wordWidth+GetCharWidth(CurrentChar)+GapWidth; + + if(lineWidth<=WrapWidth) { + if(isspace(CurrentChar) || CurrentChar=='-') + NextLine->offset=i+1, wordWidth=0; + } + else { + if(isspace(CurrentChar)) + NextLine->offset=i+1, wordWidth=0; + + lineWidth=wordWidth; + wordWidth=0; + CurrentChar=NULL; + *lineNum= *lineNum+1; + NextLine->next=new LINENode; + NextLine=NextLine->next; + } + i++; + + if(TextMsg[i]==NULL) + NextLine->offset=0; + } + +return TRUE; +} + + + +/* +int WordWrap(char *TextMsg, DWORD lineLength, WORD *lineNum, WORD *lineOffsets) { + int retVal; + __asm { + mov ebx, lineOffsets + mov ecx, lineNum + mov edx, lineLength + mov eax, TextMsg + mov esi, 0x4BC6F0 + call esi + mov retVal, eax + } +return retVal; +} +*/ + +void DeleteWordWrapList(LINENode *CurrentLine) { + LINENode *NextLine=NULL; + + while(CurrentLine!=NULL) { + NextLine=CurrentLine->next; + delete CurrentLine; + CurrentLine=NextLine; + } + } + + + +//---------------------------------------------------------------- +void DrawPCConsole() { + + DWORD NewTick = *(DWORD*)0x5709C4;//char scrn gettickcount ret + DWORD RotSpeed=*(DWORD*)0x47066B;//get rotation speed -inventory rotation speed + + if(CharRotTick>NewTick) + CharRotTick=NewTick; + + if(NewTick-CharRotTick>RotSpeed) { + CharRotTick=NewTick; + if(CharRotOri<5) + CharRotOri++; + else CharRotOri=0; + + + int WinRef=*(DWORD*)0x57060C;//char screen window ref + //BYTE *WinSurface = GetWinSurface(WinRef); + WINinfo *WinInfo=GetWinStruct(WinRef); + + BYTE *ConSurface = new BYTE [70*102]; + + sub_draw( 70, 102, 640, 480, 338, 78, CharScrnBackSurface, 70, 102, 0, 0, ConSurface, 0); + //sub_draw( 70, 102, widthBG, heightBG, xPosBG, yPosBG, BGSurface, 70, 102, 0, 0, ConSurface, 0); + + //DWORD CritNum = *(DWORD*)0x5108A4;//pointer to current base hero critter FrmId + DWORD CritNum = *(DWORD*)(*(DWORD*)0x6610B8+0x20);//pointer to current armored hero critter FrmId + DWORD CritFrmObj; + FRMhead *CritFrm; + // DWORD PcCritOri=0; + DWORD CritWidth; + DWORD CritHeight; + BYTE *CritSurface; + + CritFrm = GetFrm(LoadFrm(1, CritNum), &CritFrmObj); + CritWidth = GetFrmFrameWidth(CritFrm, 0, CharRotOri); + CritHeight = GetFrmFrameHeight(CritFrm, 0, CharRotOri); + CritSurface = GetFrmFrameSurface(CritFrm, 0, CharRotOri); + + sub_draw( CritWidth, CritHeight, CritWidth, CritHeight, 0, 0, CritSurface, 70, 102, 35-CritWidth/2, 51-CritHeight/2, ConSurface, 0); + + BYTE ConsoleGreen = *(BYTE*)0x6A3CB0;//palette offset stored in mem - text colour + BYTE ConsoleGold = *(BYTE*)0x6AB8BB;//palette offset stored in mem - text colour + + BYTE styleColour=ConsoleGreen, raceColour=ConsoleGreen; + if(*(DWORD*)0x5707D0==0x501) + raceColour=ConsoleGold; + else if(*(DWORD*)0x5707D0==0x502) + styleColour=ConsoleGold; +/* + int oldFont = GetFont();//store current font + SetFont(0x65);//set font for consol text + char TextBuf[12]; + + sprintf_s(TextBuf, 12, "%2d\0", CurrentRaceVal); + PrintText(TextBuf, raceColour, 2, 2, 64, 70, ConSurface); + + sprintf_s(TextBuf, 12, "%2d\0", CurrentStyleVal); + PrintText(TextBuf, styleColour, 5, 88, 64, 70, ConSurface); + + SetFont(oldFont);//restore previous font +*/ + + //sub_draw( 70, 102, 70, 102, 0, 0, ConSurface, 640, 480, 338, 78, WinSurface, 0); + sub_draw( 70, 102, 70, 102, 0, 0, ConSurface, WinInfo->width, WinInfo->height, 338, 78, WinInfo->surface, 0); + + UnloadFrm(CritFrmObj); + CritSurface=NULL; + delete[] ConSurface; + WinInfo=NULL; + RedrawWin(WinRef); + } + +} + + +//------------------------------------------------------------------------------------------------------------------------------------------------ +void DrawCharNote(bool Style, int WinRef, DWORD xPosWin, DWORD yPosWin, BYTE *BGSurface, DWORD xPosBG, DWORD yPosBG, DWORD widthBG, DWORD heightBG) { + + MSGList MsgList; + char *TitleMsg=NULL; + char *InfoMsg=NULL; + + char *MsgFileName=NULL; + + if(!Style)MsgFileName="game\\AppRace.msg"; + else MsgFileName="game\\AppStyle.msg"; + + if(LoadMsgList(&MsgList, MsgFileName)==1) { + TitleMsg=GetMsg(&MsgList, 100, 2); + InfoMsg=GetMsg(&MsgList, 101, 2); + } + + BYTE colour=*(BYTE*)0x6A38D0;//brown + + WINinfo *WinInfo=GetWinStruct(WinRef); + + BYTE *PadSurface; + PadSurface = new BYTE [280*168]; + sub_draw( 280, 168, widthBG, heightBG, xPosBG, yPosBG, BGSurface, 280, 168, 0, 0, PadSurface, 0); + + UNLSTDfrm *frm; + if(Style) frm=LoadUnlistedFrm("AppStyle.frm", 10); + else frm=LoadUnlistedFrm("AppRace.frm", 10); + + if(frm) { + sub_draw(frm->frames[0].width, frm->frames[0].height, frm->frames[0].width, frm->frames[0].height, 0, 0, frm->frames[0].indexBuff, 280, 168, 136, 37, PadSurface, 0);//cover buttons pics bottom + //sub_draw(frm->width, frm->height, frm->width, frm->height, 0, 0, frm->surface, 280, 168, 136, 37, PadSurface, 0);//cover buttons pics bottom + //sub_draw(frm->width, frm->height, frm->width, frm->height, 0, 0, frm->surface, 280, 168, 135, 36, PadSurface, 0);//cover buttons pics bottom + delete frm; + } + + + + int oldFont = GetFont();//store current font + SetFont(0x66);//set font for title + + DWORD textHeight=GetTextHeight(); + + if(TitleMsg!=NULL) { + PrintText(TitleMsg, colour, 0, 0, 265, 280, PadSurface); + //DrawLineX(WinRef, 348, 613, 272+textHeight, colour); + //DrawLineX(WinRef, 348, 613, 273+textHeight, colour); + memset( PadSurface+280*textHeight, colour, 265 ); + memset( PadSurface+280*(textHeight+1), colour, 265 ); + } + + SetFont(0x65);//set font for info + + textHeight=GetTextHeight(); + + DWORD lineNum=0; + + LINENode *StartLine=new LINENode; + LINENode *CurrentLine, *NextLine; + + + if(InfoMsg!=NULL) { + if(CreateWordWrapList(InfoMsg, 160, &lineNum, StartLine)) { + int lineHeight=43; + char TempChar=0; + + if(lineNum==1)PrintText(InfoMsg, colour, 0, lineHeight, 280, 280, PadSurface); + else{ + if(lineNum>11)lineNum=11; + CurrentLine=StartLine; + + for(DWORD line=0; linenext; + TempChar=InfoMsg[NextLine->offset];//[line+1]]; + InfoMsg[NextLine->offset]='\0'; + PrintText(InfoMsg+CurrentLine->offset, colour, 0, lineHeight, 280, 280, PadSurface); + InfoMsg[NextLine->offset]=TempChar; + lineHeight=lineHeight+textHeight+1; + CurrentLine=NextLine; + } + } + } + } + + sub_draw( 280, 168, 280, 168, 0, 0, PadSurface, WinInfo->width, WinInfo->height, xPosWin, yPosWin, WinInfo->surface, 0); + + + DeleteWordWrapList(StartLine); + CurrentLine=NULL; + NextLine=NULL; + delete[]PadSurface; + WinInfo=NULL; + SetFont(oldFont);//restore previous font + DestroyMsgList(&MsgList); +//RedrawWin(*(DWORD*)0x57060C); +} + + +/* +void DrawCharNote(DWORD LstNum, char *TitleTxt, char *AltTitleTxt, char *Message) { + + __asm { + MOV ECX,Message//100//DWORD PTR DS:[0x5705CC] + MOV EBX,AltTitleTxt//DWORD PTR DS:[0x5705BC] + MOV EDX,TitleTxt//DWORD PTR DS:[0x5705B8] + MOV EAX,LstNum//11//LstNum//DWORD PTR DS:[0x5705B0] + mov esi, 0x43AAEC + CALL esi + } + +} +*/ + + +//---------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void _stdcall HeroSelectWindow(int RaceStyleFlag) { + +if(!AppModEnabled)return; + + bool isStyle=TRUE; + if(RaceStyleFlag==0)isStyle=FALSE; + + DWORD ResWidth=*(DWORD*)0x4CAD6B; + DWORD ResHeight=*(DWORD*)0x4CAD66; + + int WinRef = CreateWin(ResWidth/2-242, (ResHeight-100)/2-65, 484, 230, 100, 0x4); + if(WinRef==-1)return; + + int mouseWasHidden=IsMouseHidden(); + if(mouseWasHidden) + ShowMouse(); + + int oldMouse = GetMousePic(); + SetMousePic(1); + + + BYTE *WinSurface = GetWinSurface(WinRef); + + BYTE *mainSurface; + mainSurface = new BYTE [484*230]; + + DWORD tempObj; + BYTE *tempSurface; + //perkwin + tempSurface=GetFrmSurface(LoadFrm(6, 86), 0, 0, &tempObj); + sub_draw( 484, 230, 573, 230, 89, 0, tempSurface, 484, 230, 0, 0, mainSurface, 0); + sub_draw( 13, 230, 573, 230, 0, 0, tempSurface, 484, 230, 0, 0, mainSurface, 0); + UnloadFrm(tempObj); + + //opbase + tempSurface=GetFrmSurface(LoadFrm(6, 220), 0, 0, &tempObj); + sub_draw( 164, 217, 164, 217, 0, 0, tempSurface, 484, 230, 12, 4, mainSurface, 0); + UnloadFrm(tempObj); + + //use + tempSurface=GetFrmSurface(LoadFrm(6, 113), 0, 0, &tempObj); + sub_draw( 138, 132, 292, 376, 128, 20, tempSurface, 484, 230, 25, 38, mainSurface, 0); + sub_draw( 2, 132, 292, 376, 23, 224, tempSurface, 484, 230, 25, 38, mainSurface, 0); + sub_draw( 12, 4, 292, 376, 135, 148, tempSurface, 484, 230, 25, 166, mainSurface, 0); + UnloadFrm(tempObj); + + //barter + tempSurface=GetFrmSurface(LoadFrm(6, 111), 0, 0, &tempObj); + sub_draw(25, 52, 640, 191, 190, 54, tempSurface, 484, 230, 27, 57, mainSurface, 0);//button background up down + UnloadFrm(tempObj); + + //loot + tempSurface=GetFrmSurface(LoadFrm(6, 114), 0, 0, &tempObj); + sub_draw(116, 27, 537, 376, 392, 325, tempSurface, 484, 230, 36, 180, mainSurface, 0);//button background "done" + UnloadFrm(tempObj); + + + DWORD MenuUObj, MenuDObj; + BYTE *MenuUSurface=GetFrmSurface(LoadFrm(6, 299), 0, 0, &MenuUObj);//MENUUP Frm + BYTE *MenuDSurface=GetFrmSurface(LoadFrm(6, 300), 0, 0, &MenuDObj);//MENUDOWN Frm + CreateButton(WinRef, 116, 181, 26, 26, -1, -1, -1, 0x0D, MenuUSurface, MenuDSurface, 0x20); + + + + DWORD DidownUObj, DidownDObj; + BYTE *DidownUSurface=GetFrmSurface(LoadFrm(6, 93), 0, 0, &DidownUObj);//MENUUP Frm + BYTE *DidownDSurface=GetFrmSurface(LoadFrm(6, 94), 0, 0, &DidownDObj);//MENUDOWN Frm + CreateButton(WinRef, 28, 84, 24, 25, -1, -1, -1, 0x150, DidownUSurface, DidownDSurface, 0x20); + + DWORD DiupUObj, DiupDObj; + BYTE *DiupUSurface=GetFrmSurface(LoadFrm(6, 100), 0, 0, &DiupUObj);//MENUUP Frm + BYTE *DiupDSurface=GetFrmSurface(LoadFrm(6, 101), 0, 0, &DiupDObj);//MENUDOWN Frm + CreateButton(WinRef, 28, 59, 23, 24, -1, -1, -1, 0x148, DiupUSurface, DiupDSurface, 0x20); + + + int oldFont; + oldFont = GetFont(); + SetFont(0x67); + BYTE textColour = *(BYTE*)0x6A82F3;//PeanutButter colour -palette offset stored in mem + + + char titleText[8]; + DWORD titleTextWidth; + //Get alternate text from ini if available + if(isStyle)GetPrivateProfileString("AppearanceMod", "StyleText", "Style", titleText, 8, translationIni); + else GetPrivateProfileString("AppearanceMod", "RaceText", "Race", titleText, 8, translationIni); + + titleTextWidth=GetTextWidth(titleText); + + PrintText(titleText, textColour, 94-titleTextWidth/2, 10, titleTextWidth, 484, mainSurface); + + + DWORD titleTextHeight=GetTextHeight(); + //Title underline + memset( mainSurface+484*(10+titleTextHeight)+94-titleTextWidth/2, textColour, titleTextWidth ); + memset( mainSurface+484*(10+titleTextHeight+1)+94-titleTextWidth/2, textColour, titleTextWidth ); + + + sub_draw( 484, 230, 484, 230, 0, 0, mainSurface, 484, 230, 0, 0, WinSurface, 0); + + ShowWin(WinRef); + + int raceVal=CurrentRaceVal, styleVal=CurrentStyleVal;//show default style when setting race + if(!isStyle)styleVal=0; + LoadHeroDat(raceVal, styleVal); + + BYTE *ConDraw; + ConDraw = new BYTE [70*102]; + +// char TextBuf[12]; + + DWORD NewTick=0, OldTick=0; + + textColour=*(BYTE*)0x6A3CB0;//ConsoleGreen colour -palette offset stored in mem + SetFont(0x65); + + DWORD CritNum = *(DWORD*)0x5108A4;//pointer to current base hero critter FrmID + //DWORD CritNum = *(DWORD*)(*(DWORD*)0x6610B8+0x20);//pointer to current armored hero critter FrmID + FRMhead *CritFrm; + DWORD CritFrmObj=0, CritOri=0, CritWidth=0, CritHeight=0; + BYTE *CritSurface=NULL; + + int button=0, exitMenu=0; + + bool drawFlag=TRUE;//redraw flag for char note pad + + DWORD RotSpeed=*(DWORD*)0x47066B;//get rotation speed -inventory rotation speed + + DWORD RedrawTick=0; + + while(!exitMenu) {//main loop + + + NewTick = GetTickCount();//timer for redraw + if(OldTick>NewTick) + OldTick=NewTick; + + if(NewTick-OldTick>RotSpeed) {//time to rotate critter + OldTick=NewTick; + if(CritOri<5) + CritOri++; + else CritOri=0; + } + + if(RedrawTick>NewTick) + RedrawTick=NewTick; + + if(NewTick-RedrawTick>60) {//time to redraw + RedrawTick=NewTick; + sub_draw( 70, 102, 484, 230, 66, 53, mainSurface, 70, 102, 0, 0, ConDraw, 0); + + CritFrm = GetFrm(LoadFrm(1, CritNum), &CritFrmObj); + CritWidth = GetFrmFrameWidth(CritFrm, 0, CritOri); + CritHeight = GetFrmFrameHeight(CritFrm, 0, CritOri); + CritSurface = GetFrmFrameSurface(CritFrm, 0, CritOri); + sub_draw( CritWidth, CritHeight, CritWidth, CritHeight, 0, 0, CritSurface, 70, 102, 35-CritWidth/2, 51-CritHeight/2, ConDraw, 0); + UnloadFrm(CritFrmObj); + CritSurface=NULL; +/* + if(isStyle)sprintf_s(TextBuf, 12, "%2d\0", styleVal); + else sprintf_s(TextBuf, 12, "%2d\0", raceVal); + + PrintText(TextBuf, textColour, 2, 2, 64, 70, ConDraw); +*/ + sub_draw( 70, 102, 70, 102, 0, 0, ConDraw, 484, 230, 66, 53, WinSurface, 0); + + if(drawFlag==TRUE) + DrawCharNote(isStyle, WinRef, 190, 29, mainSurface, 190, 29, 484, 230); + drawFlag=FALSE; + + RedrawWin(WinRef); + } + + + + button = check_buttons(); + if(button==0x148) {//previous style/race -up arrow button pushed + drawFlag=TRUE; + PlayAcm("ib1p1xx1"); + RefreshArtCache(); + + if(isStyle) { + if(styleVal>0)styleVal--; + if(LoadHeroDat(raceVal, styleVal)!=0) { + styleVal=0; + LoadHeroDat(raceVal, styleVal); + } + } + else {//Race + if(raceVal>0)styleVal=0, raceVal--; + if(LoadHeroDat(raceVal, styleVal)!=0) { + raceVal=0; + LoadHeroDat(raceVal, styleVal); + } + + } + } + else if(button==0x150) {//Next style/race -down arrow button pushed + drawFlag=TRUE; + PlayAcm("ib1p1xx1"); + RefreshArtCache(); + + if(isStyle) { + styleVal++; + if(LoadHeroDat(raceVal, styleVal)!=0) { + styleVal--; + LoadHeroDat(raceVal, styleVal); + } + } + else {//Race + styleVal=0, raceVal++; + if(LoadHeroDat(raceVal, styleVal)!=0) { + raceVal--; + LoadHeroDat(raceVal, styleVal); + } + } + } + else if(button==0x0D) {//save and exit -Enter button pushed + exitMenu=-1; + if(!isStyle && CurrentRaceVal==raceVal)//return style to previous value if no race change + styleVal=CurrentStyleVal; + + CurrentRaceVal=raceVal; + CurrentStyleVal=styleVal; + } + else if(button==0x1B)//exit -ESC button pushed + exitMenu=-1; + + } + + RefreshArtCache(); + LoadHeroDat(CurrentRaceVal, CurrentStyleVal); + SetAppearanceGlobals(CurrentRaceVal, CurrentStyleVal); + + DestroyWin(WinRef); + delete[]mainSurface; + delete[]ConDraw; + UnloadFrm(MenuUObj); + UnloadFrm(MenuDObj); + MenuUSurface=NULL; + MenuDSurface=NULL; + + UnloadFrm(DidownUObj); + UnloadFrm(DidownDObj); + DidownUSurface=NULL; + DidownDSurface=NULL; + + UnloadFrm(DiupUObj); + UnloadFrm(DiupDObj); + DiupUSurface=NULL; + DiupDSurface=NULL; + + SetFont(oldFont); + SetMousePic(oldMouse); + + if(mouseWasHidden) + HideMouse(); +} + + +void FixTextHighLight() { + + __asm { + //redraw special text + mov eax,7 + xor ebx,ebx + xor edx,edx + mov esi, 0x434B38 + call esi + //redraw trait options text + mov esi, 0x43B8A8 + call esi + //redraw skills text + xor eax,eax + mov esi, 0x436154 + call esi + //redraw level text + mov esi, 0x434920 + call esi + //redraw perks, karma, kill text + mov esi, 0x43410C + call esi + //redraw hit points to crit chance text + mov esi, 0x43527C + call esi + //redraw note pad area text + //mov esi, 0x4365AC + //call esi + } +} + +//------------------------------------------- +void _stdcall DrawCharNoteNewChar(bool Style) { + DrawCharNote(Style, *(DWORD*)0x57060C, 348, 272, CharScrnBackSurface, 348, 272, 640, 480); +} + +//------------------------------------------------------------------- +int _stdcall CheckCharButtons() { + + int button=check_buttons(); + + int raceVal=CurrentRaceVal; + int styleVal=CurrentStyleVal; + + int drawFlag=-1; + + if(*(DWORD*)0x5707D0==0x503)button=0x501; + else if(*(DWORD*)0x5707D0==0x504)button=0x502; + else if(*(DWORD*)0x5707D0==0x501 || *(DWORD*)0x5707D0==0x502) { + switch(button) { + case 0x14B://button =left + case 0x14D://button =right + if(*(DWORD*)0x5709D0==1) {//if in char creation scrn + + if(*(DWORD*)0x5707D0==0x501) + button=button+0x3C6; + else if(*(DWORD*)0x5707D0==0x502) + button=button+0x3C6+1; + } + break; + case 0x148://button =up + case 0x150://button =down + if(*(DWORD*)0x5707D0==0x501) + button=0x502; + else if(*(DWORD*)0x5707D0==0x502) + button=0x501; + break; + case 0x0D://button =return + case 0x1B://button =esc + case 0x1F4://button =done + case 'd'://button =done + case 'D'://button =done + case 0x1F6://button =cancel + case 'c'://button =cancel + case 'C'://button =cancel + if(*(DWORD*)0x5707D0==0x501)//for redrawing note when reentering char screen + *(DWORD*)0x5707D0=0x503; + else + *(DWORD*)0x5707D0=0x504; + break; + + default : + break; + } + } + + + switch(button) { + case 0x9://tab button pushed + if(*(DWORD*)0x5707D0>=0x3D && *(DWORD*)0x5707D0<0x4F)//if menu ref in last menu go to race + button=0x501,drawFlag=0; + break; + case 0x501://race button pushed + drawFlag=0; + break; + case 0x502://style button pushed + drawFlag=1; + break; + case 0x511://race left button pushed + RefreshArtCache(); + + if(raceVal>0)styleVal=0, raceVal--; + + if(LoadHeroDat(raceVal, styleVal)!=0) { + raceVal=0; + LoadHeroDat(raceVal, styleVal); + } + drawFlag=0; + break; + case 0x513://race right button pushed + RefreshArtCache(); + + styleVal=0, raceVal++; + if(LoadHeroDat(raceVal, styleVal)!=0) { + raceVal--; + LoadHeroDat(raceVal, styleVal); + } + drawFlag=0; + break; + case 0x512://style left button pushed + RefreshArtCache(); + + if(styleVal>0)styleVal--; + + if(LoadHeroDat(raceVal, styleVal)!=0) { + styleVal=0; + LoadHeroDat(raceVal, styleVal); + } + drawFlag=1; + break; + case 0x514://style right button pushed + RefreshArtCache(); + + styleVal++; + if(LoadHeroDat(raceVal, styleVal)!=0) { + styleVal--; + LoadHeroDat(raceVal, styleVal); + } + drawFlag=1; + break; + default : + break; + } + CurrentRaceVal=raceVal; + CurrentStyleVal=styleVal; + + + + + if(drawFlag==1) { + PlayAcm("ib3p1xx1"); + *(DWORD*)0x5707D0=0x502; + FixTextHighLight(); + DrawCharNoteNewChar(1); + //DrawCharNote(1, *(DWORD*)0x57060C, 348, 272, CharScrnBackSurface, 348, 272, 640, 480); + } + else if(drawFlag==0) { + PlayAcm("ib3p1xx1"); + *(DWORD*)0x5707D0=0x501; + FixTextHighLight(); + DrawCharNoteNewChar(0); + //DrawCharNote(0, *(DWORD*)0x57060C, 348, 272, CharScrnBackSurface, 348, 272, 640, 480); + } + + DrawPCConsole();//(*(DWORD*)0x57060C, 338, 78, CharScrnBackSurface, 338, 78, 640, 480); + + return button; +} + + +//------------------------------------------ +static void __declspec(naked) CheckCharScrnButtons(void) { + + __asm { + call CheckCharButtons + cmp eax, 0x500 + jl EndFunc + cmp eax, 0x515 + jg EndFunc + pop eax//ditch old ret addr + push 0x431E8A//recheck buttons if app mod button + EndFunc: + ret + } +} + +//------------------------------- +void DeleteCharSurfaces() { + delete[]NewButt01Surface; + NewButt01Surface=NULL; + delete[]CharScrnBackSurface; + CharScrnBackSurface=NULL; +} + +//------------------------------------------ +static void __declspec(naked) CharScrnEnd(void) { + + __asm { + pushad + call DeleteCharSurfaces + popad + MOV EBP,DWORD PTR DS:[0x5707D0] + retn + } +} + + +//------------------------------------------ +static void __declspec(naked) SexScrnEnd(void) { + + __asm { + pushad + MOV EDX,0x22 + MOV EAX,DWORD PTR DS:[0x6610B8] + mov ebx, 0x4AEF48 //get PC stat current gender + CALL ebx + mov ecx, eax + mov ebx, 0x437664 + call ebx //call gender selection window + MOV EDX,0x22 + MOV EAX,DWORD PTR DS:[0x6610B8] + mov ebx, 0x4AEF48 //get PC stat current gender + CALL ebx + cmp ecx, eax //check if gender has been changed + je EndFunc + + xor ebx, ebx +// cmp byte ptr ds:[0x596C7B],1//check if wearing vault suit +// jne NoVaultSuit +// mov ebx, 0x8 +//NoVaultSuit: + test eax, eax// check if male 0 + jnz IsFemale + mov eax,dword ptr ds:[ebx+0x5108A8]//base male model + jmp ChangeSex + IsFemale: + mov eax,dword ptr ds:[ebx+0x5108AC]//base female model + ChangeSex: + call AdjustHeroBaseArt + //mov dword ptr ds:[0x5108A4],eax//current base dude model + mov eax,dword ptr ds:[0x6610B8]//dude state structure + mov ebx, 0x471C08 + call ebx + mov CurrentRaceVal, 0//reset race and style to defaults + mov CurrentStyleVal, 0 + + push CurrentStyleVal + push CurrentRaceVal + call LoadHeroDat + call RefreshPCArt + //Check If Race or Style selected to redraw info note + cmp dword ptr ds:[0x5707D0], 0x501 + jne CheckIfStyle + push 0 + call DrawCharNoteNewChar + CheckIfStyle: + cmp dword ptr ds:[0x5707D0], 0x502 + jne EndFunc + push 1 + call DrawCharNoteNewChar + + EndFunc: + popad + retn + } +} + + +//------------------------------------------ +static void __declspec(naked) AddCharScrnButtons(void) { + + __asm { + push ebp // prolog + mov ebp, esp + sub esp, __LOCAL_SIZE + pushad + } + + int WinRef; + WinRef=*(DWORD*)0x57060C;//char screen window ref + + //race and style buttons + CreateButton(WinRef, 332, 0, 82, 32, -1, -1, 0x501, -1, 0, 0, 0); + CreateButton(WinRef, 332, 226, 82, 32, -1, -1, 0x502, -1, 0, 0, 0); + + if(*(DWORD*)0x5709D0==1) {//equals 1 if new char screen - equals 0 if ingame char screen + + //reset hero appearance + // RefreshArtCache(); + //CurrentRaceVal=0; + //CurrentStyleVal=0; + //LoadHeroDat(CurrentRaceVal, CurrentStyleVal); + // RefreshPCArt(); + + if(NewButt01Surface==NULL) { + NewButt01Surface = new BYTE [20*18*4]; + + DWORD FrmObj;//frm objects for char screen Appearance button + BYTE *FrmSurface; + + FrmSurface=GetFrmSurface(LoadFrm(6, 122), 0, 0, &FrmObj);//SLUFrm + sub_draw(20, 18, 20, 18, 0, 0, FrmSurface, 20, 18*4, 0, 0, NewButt01Surface, 0x0); + UnloadFrm(FrmObj); + FrmSurface=GetFrmSurface(LoadFrm(6, 123), 0, 0, &FrmObj);//SLDFrm + sub_draw(20, 18, 20, 18, 0, 0, FrmSurface, 20, 18*4, 0, 18, NewButt01Surface, 0x0); + UnloadFrm(FrmObj); + FrmSurface=GetFrmSurface(LoadFrm(6, 124), 0, 0, &FrmObj);//SRUFrm + sub_draw(20, 18, 20, 18, 0, 0, FrmSurface, 20, 18*4, 0, 18*2, NewButt01Surface, 0x0); + UnloadFrm(FrmObj); + FrmSurface=GetFrmSurface(LoadFrm(6, 125), 0, 0, &FrmObj);//SRDFrm + sub_draw(20, 18, 20, 18, 0, 0, FrmSurface, 20, 18*4, 0, 18*3, NewButt01Surface, 0x0); + UnloadFrm(FrmObj); + FrmSurface=NULL; + } + + + //check if Data exists for other races male or female, and if so enable race selection buttons. + if(GetFileAttributes("Appearance\\hmR01S00\0")!=INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR01S00\0")!=INVALID_FILE_ATTRIBUTES || + GetFileAttributes("Appearance\\hmR01S00.dat\0")!=INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR01S00.dat\0")!=INVALID_FILE_ATTRIBUTES) { + //race selection buttons + CreateButton(WinRef, 348, 37, 20, 18, -1, -1, -1, 0x511, NewButt01Surface, NewButt01Surface+(20*18), 0x20); + CreateButton(WinRef, 373, 37, 20, 18, -1, -1, -1, 0x513, NewButt01Surface+(20*18*2), NewButt01Surface+(20*18*3), 0x20); + } + //style selection buttons + CreateButton(WinRef, 348, 199, 20, 18, -1, -1, -1, 0x512, NewButt01Surface, NewButt01Surface+(20*18), 0x20); + CreateButton(WinRef, 373, 199, 20, 18, -1, -1, -1, 0x514, NewButt01Surface+(20*18*2), NewButt01Surface+(20*18*3), 0x20); + } + + + __asm { + popad + mov esp, ebp // epilog + pop ebp + //move tag skills button to fit Appearance interface + mov edx, 0x1AA;//0x18C+36 was 0x18C tag/skills button xpos + retn + } + + + } + + + +//------------------------------------------ +static void __declspec(naked) FixCharScrnBack(void) { +//00432B92 |. A3 A4075700 MOV DWORD PTR DS:[5707A4],EAX + __asm { + MOV DWORD PTR DS:[0x5707A4],EAX//surface ptr for char scrn back + test eax, eax//check if frm loaded ok + je EndFunc + + push ebp // prolog + mov ebp, esp + sub esp, __LOCAL_SIZE + pushad + } + + + + if(CharScrnBackSurface==NULL) { + CharScrnBackSurface = new BYTE [640*480]; + + BYTE *OldCharScrnBackSurface; + OldCharScrnBackSurface = *(BYTE**)0x5707A4;//char screen background frm surface + + //CharScrnBackSurface=*(BYTE**)0x5707A4;//char screen background frm surface + + //copy old charscrn surface to new + sub_draw(640, 480, 640, 480, 0, 0, OldCharScrnBackSurface, 640, 480, 0, 0, CharScrnBackSurface, 0); + + //copy Tag Skill Counter background to the right + sub_draw(38, 26, 640, 480, 519, 228, OldCharScrnBackSurface, 640, 480, 519+36, 228, CharScrnBackSurface, 0); + //copy a blank part of the Tag Skill Bar hiding the old counter + sub_draw(38, 26, 640, 480, 460, 228, OldCharScrnBackSurface, 640, 480, 519, 228, CharScrnBackSurface, 0); + + sub_draw(36, 258, 640, 480, 332, 0, OldCharScrnBackSurface, 640, 480, 408, 0, CharScrnBackSurface, 0);//shift behind button rail + sub_draw(6, 32, 640, 480, 331, 233, OldCharScrnBackSurface, 640, 480, 330, 6, CharScrnBackSurface, 0);//shadow for style/race button + + + DWORD FrmObj, FrmMaskObj;//frm objects for char screen Appearance button + BYTE *FrmSurface,*FrmMaskSurface; + + FrmSurface=GetFrmSurface(LoadFrm(6, 113), 0, 0, &FrmObj); + sub_draw(81, 132, 292, 376, 163, 20, FrmSurface, 640, 480, 331, 63, CharScrnBackSurface, 0);//char view win + sub_draw(79, 31, 292, 376, 154, 228, FrmSurface, 640, 480, 331, 32, CharScrnBackSurface, 0);//upper char view win + sub_draw(79, 30, 292, 376, 158, 236, FrmSurface, 640, 480, 331, 195, CharScrnBackSurface, 0);//lower char view win + UnloadFrm(FrmObj); + + //Sexoff Frm + FrmSurface=GetFrmSurface(LoadFrm(6, 188), 0, 0, &FrmObj); + //Sex button mask frm + FrmMaskSurface=GetFrmSurface(LoadFrm(6, 187), 0, 0, &FrmMaskObj); + + sub_draw(80, 28, 80, 32, 0, 0, FrmMaskSurface, 80, 32, 0, 0, FrmSurface, 0x39);//mask for style and race buttons + UnloadFrm(FrmMaskObj); + FrmMaskSurface=NULL; + + FrmSurface[80*32-1]=0; + FrmSurface[80*31-1]=0; + FrmSurface[80*30-1]=0; + + FrmSurface[80*32-2]=0; + FrmSurface[80*31-2]=0; + FrmSurface[80*30-2]=0; + + FrmSurface[80*32-3]=0; + FrmSurface[80*31-3]=0; + FrmSurface[80*30-3]=0; + + FrmSurface[80*32-4]=0; + FrmSurface[80*31-4]=0; + FrmSurface[80*30-4]=0; + + sub_draw(80, 32, 80, 32, 0, 0, FrmSurface, 640, 480, 332, 0, CharScrnBackSurface, 0);//style and race buttons + sub_draw(80, 32, 80, 32, 0, 0, FrmSurface, 640, 480, 332, 225, CharScrnBackSurface, 0);//style and race buttons + UnloadFrm(FrmObj); + + //frm background for char screen Appearance button + FrmSurface=GetFrmSurface(LoadFrm(6, 174), 0, 0, &FrmObj);//Pickchar frm + sub_draw(69, 20, 640, 480, 282, 320, FrmSurface, 640, 480, 337, 37, CharScrnBackSurface, 0);//button backround top + sub_draw(69, 20, 640, 480, 282, 320, FrmSurface, 640, 480, 337, 199, CharScrnBackSurface, 0);//button backround bottom + sub_draw(47, 16, 640, 480, 94, 394, FrmSurface, 640, 480, 347, 39, CharScrnBackSurface, 0);//cover buttons pics top + sub_draw(47, 16, 640, 480, 94, 394, FrmSurface, 640, 480, 347, 201, CharScrnBackSurface, 0);//cover buttons pics bottom + UnloadFrm(FrmObj); + FrmSurface=NULL; + + int oldFont; + oldFont = GetFont(); + SetFont(0x67); + BYTE PeanutButter; + PeanutButter = *(BYTE*)0x6A82F3;//palette offset stored in mem + char RaceText[8], StyleText[8]; + DWORD raceTextWidth, styleTextWidth; + + //Get alternate text from ini if available + GetPrivateProfileString("AppearanceMod", "RaceText", "Race", RaceText, 8, translationIni); + GetPrivateProfileString("AppearanceMod", "StyleText", "Style", StyleText, 8, translationIni); + + raceTextWidth=GetTextWidth(RaceText); + styleTextWidth=GetTextWidth(StyleText); + + PrintText(RaceText, PeanutButter, 372-raceTextWidth/2, 6, raceTextWidth, 640, CharScrnBackSurface); + PrintText(StyleText, PeanutButter, 372-styleTextWidth/2, 231, styleTextWidth, 640, CharScrnBackSurface); + SetFont(oldFont); + + } + + __asm { + + popad + mov esp, ebp // epilog + pop ebp + mov eax, CharScrnBackSurface + EndFunc: + MOV DWORD PTR DS:[0x5707A4],EAX//surface ptr for char scrn back + retn + } + + + } + + +//Adjust PC SFX Name--------------------------------------------- +static void __declspec(naked) FixPcSFX() { +//Skip Underscore char at the start of PC App Name + __asm { + mov ah, byte ptr ds:[ebx] + cmp ah, 0x5F//check if Name begins with an '_' character + jne ExitFunc + add ebx, 1//shift address to next char + ExitFunc: + //restore original code + mov eax, ebx + cmp dword ptr ds:[0x518E30], 0 + ret + } + +} + +/* +//Set path to normal before printing or saving character details------------ +static void __declspec(naked) FixCharScrnSaveNPrint() { +//00432359 |> E8 AA580000 |CALL fallout2.00437C08 + + __asm { + push TempPathPtr//store current path + mov eax, 0x6B24D0 + mov TempPathPtr, eax//set path to normal + push esi + mov esi, 0x437C08//call char-scrn menu function + call esi + pop esi + pop TempPathPtr //restore stored path + + call RefreshPCArt + ret + } + +} +*/ + + + +// Load Appearance data from GCD file------------ +void _stdcall LoadGCDAppearance(void *FileStream) { + CurrentRaceVal=0; + CurrentStyleVal=0; + DWORD temp; + if(FReadDword(FileStream, &temp)!=-1) { + CurrentRaceVal=(int)temp; + if(FReadDword(FileStream, &temp)!=-1) + CurrentStyleVal=(int)temp; + } + + //reset hero appearance + RefreshArtCache(); + LoadHeroDat(CurrentRaceVal, CurrentStyleVal); + RefreshPCArt(); + + FCloseFile(FileStream); +} + + +// Save Appearance data to GCD file-------------- +void _stdcall SaveGCDAppearance(void *FileStream) { + + + if(FWriteDword(FileStream, (DWORD)CurrentRaceVal)!=-1) + FWriteDword(FileStream, (DWORD)CurrentStyleVal); + + FCloseFile(FileStream); +} + + +// Reset Appearance when selecting "Create Character" from the New Char screen------ +static void __declspec(naked) CreateCharReset() { + + __asm { + mov CurrentRaceVal, 0//reset race and style to defaults + mov CurrentStyleVal, 0 + + push CurrentStyleVal + push CurrentRaceVal + call LoadHeroDat + call RefreshPCArt + + mov eax, 1 + ret + } + +} + + +//--------------------------------- +void HeroAppearanceModExit() { + if(!AppModEnabled)return; + + if(HeroPathPtr) { + delete[] HeroPathPtr->path; + delete HeroPathPtr; + } + if(RacePathPtr) { + delete[] RacePathPtr->path; + delete RacePathPtr; + } +} + + +//------------------------------------------------- +static void __declspec(naked) FixPcCriticalHitMsg() { + __asm { + and eax, 0x00000FFF + cmp eax, critterListSize//check if critter art in PC range + jle EndFunc + sub eax, critterListSize//shift critter art index down out of hero range + EndFunc: + ret + } + +} + + +//-------------------------------------------------------------------------- +void EnableHeroAppearanceMod() +{ + AppModEnabled=true; + + //setup paths + HeroPathPtr=new sPath; + RacePathPtr=new sPath; + HeroPathPtr->path=new char[64]; + RacePathPtr->path=new char[64]; + + HeroPathPtr->isDat=0; + RacePathPtr->isDat=0; + HeroPathPtr->pDat=NULL; + RacePathPtr->pDat=NULL; + + //Check if new Appearance char scrn button pushed + SafeWrite32(0x431E9E, (DWORD)&CheckCharScrnButtons - 0x431EA2); + + //Destroy new Appearance button mem after use + SafeWrite16(0x4329D8, 0xE890); + SafeWrite32(0x4329DA, (DWORD)&CharScrnEnd - 0x4329DE); + + //Check if sex has changed and reset char appearance + SafeWrite8(0x4322E8, 0xe8); + SafeWrite32(0x4322E9, (DWORD)&SexScrnEnd - 0x4322ED); + + //Load New Hero Art + SafeWrite16(0x4DEEE5, 0xE890); + SafeWrite32(0x4DEEE7, (DWORD)&LoadNewHeroArt - 0x4DEEEB); + + //Divert critter frm file name function exit for file checking + SafeWrite8(0x419520, 0xEB);//divert func exit + SafeWrite32(0x419521, 0x9090903E); + + //Check if new hero art exists otherwise use regular art + SafeWrite8(0x419560, 0xE8); + SafeWrite32(0x419561, (DWORD)&CheckHeroExist - 0x419565); + + //Double size of critter art index creating a new area for hero art + SafeWrite16(0x4196AA, 0xE890); + SafeWrite32(0x4196AC, (DWORD)&DoubleArt - 0x4196B0); + + //Add new hero critter names at end of critter list + SafeWrite8(0x418B39, 0xE8); + SafeWrite32(0x418B3A, (DWORD)&AddHeroCritNames - 0x418B3E); + + //Shift base hero critter art offset up into hero section + SafeWrite8(0x49F9DA, 0xE8); + SafeWrite32(0x49F9DB, (DWORD)&AdjustHeroBaseArt - 0x49F9DF); + + //Adjust hero art index offset when changing armor + //SafeWrite8(0x4717D6, 0xE8); + //SafeWrite32(0x4717D7, (DWORD)&AdjustHeroArmorArt - 0x4717DB); + SafeWrite32(0x4717D2, (DWORD)&AdjustHeroArmorArt - 0x4717D6); + + //Hijack Save Hero State Structure fuction address 9CD54800 + //Return hero art index offset back to normal before saving + SafeWrite32(0x519400, (DWORD)&SavCritNumFix); + + + + //Tag Skills text x pos + SafeWrite32(0x433372, 0x24826+36);//Tag Skills text x pos1 + SafeWrite32(0x4362BE, 0x24826+36);//Tag Skills text x pos2 + SafeWrite32(0x4362F2, 0x20A+36);//Tag Skills num counter2 x pos1 + SafeWrite32(0x43631E, 0x20A+36);//Tag Skills num counter2 x pos2 + + //Add new Appearance mod buttons + SafeWrite8(0x43A788, 0xe8); + SafeWrite32(0x43A789, (DWORD)&AddCharScrnButtons - 0x43A78D); + + //Mod char scrn background and add new app mod graphics. also adjust tag/skill button x pos + SafeWrite8(0x432B92, 0xe8); + SafeWrite32(0x432B93, (DWORD)&FixCharScrnBack - 0x432B97); + + //skill points + SafeWrite32(0x436262, 0x24810+36);//Skill Points text x pos + SafeWrite32(0x43628A, 0x20A+36);//Skill Points num counter x pos1 + SafeWrite32(0x43B5B2, 0x20A+36);//Skill Points num counter x pos2 + + //make room for char view window + SafeWrite32(0x433678, 347+76);//shift skill buttons right 80 + SafeWrite32(0x4363CE, 380+68);//shift skill name text right 80 + SafeWrite32(0x43641C, 573+10);//shift skill % num text right 80 + SafeWrite32(0x43A74C, 223-76+10);// skill list mouse area button width + SafeWrite32(0x43A75B, 370+76);// skill list mouse area button xpos + SafeWrite32(0x436220, 0x0DFC+68);//"skill" text xpos + SafeWrite32(0x43A71E, 0xDF-68);//skill button width + SafeWrite32(0x43A72A, 0x178+68);//skill button xpos + + //redraw area for skill list + SafeWrite32(0x4361C4, 370+76);//xpos + SafeWrite32(0x4361D9, 270-76);//width + SafeWrite32(0x4361DE, 370+76);//xpos + + //skill slider thingy + SafeWrite32(0x43647C, 592+3);//xpos + SafeWrite32(0x4364FA, 614+3);//plus button xpos + SafeWrite32(0x436567, 614+3);//minus button xpos + + + //fix for Char Screen note position was x484 y309 now x383 y308 + //SafeWrite32(0x43AB55, 308*640+483);//minus button xpos + + + //Adjust PC SFX Name + SafeWrite8(0x4510EB, 0xE8); + SafeWrite32(0x4510EC, (DWORD)&FixPcSFX - 0x4510F0); + SafeWrite16(0x4510F0, 0x9090); + + + //Set path to normal before printing or saving character details + //SafeWrite32(0x43235A, (DWORD)&FixCharScrnSaveNPrint - 0x43235E); + + + +// Load Appearance data from GCD file------------ + SafeWrite16(0x42DF5D, 0x9056);//push esi "*FileStream" + SafeWrite32(0x42DF60, (DWORD)&LoadGCDAppearance - 0x42DF64); + + + +// Save Appearance data to GCD file------------ + SafeWrite16(0x42E161, 0x9056);//push esi "*FileStream" + SafeWrite32(0x42E164, (DWORD)&SaveGCDAppearance - 0x42E168); + + +// Reset Appearance when selecting "Create Character" from the New Char screen------ + SafeWrite8(0x4A7405, 0xE8); + SafeWrite32(0x4A7406, (DWORD)&CreateCharReset - 0x4A740A); + +// Fixes missing console critical hit messages when PC is attacked. +//00426135 |. 25 FF0F0000 AND EAX,00000FFF + SafeWrite8(0x426135, 0xE8); + SafeWrite32(0x426136, (DWORD)&FixPcCriticalHitMsg - 0x42613A); + +// Force Criticals For Testing +//00423A8F |. /2E:FF249D 7C374200 JMP DWORD PTR CS:[EBX*4+42377C] + //SafeWrite32(0x423A8F, 0x90909090); + //SafeWrite32(0x423A93, 0x90909090); + +} + + + + diff --git a/sfall/HeroAppearance.h b/sfall/HeroAppearance.h new file mode 100644 index 00000000..d1f6e1d2 --- /dev/null +++ b/sfall/HeroAppearance.h @@ -0,0 +1,62 @@ +/* + * sfall + * Copyright (C) 2009 Mash (Matt Wells, mashw at bigpond dot net dot au) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + + +//for holding window info +typedef struct WINinfo { + DWORD ref; + DWORD flags; + RECT wRect; + DWORD width; + DWORD height; + DWORD clearColour; + DWORD unknown2; + DWORD unknown3; + BYTE *surface; // bytes frame data ref to palette + DWORD buttonListP; + DWORD unknown5;//buttonptr? + DWORD unknown6; + DWORD unknown7; + DWORD drawFuncP; + } WINinfo; + + + + + +WINinfo *GetWinStruct(int WinRef); +void RedrawWin(int WinRef); +void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface); +DWORD GetTextWidth(char *TextMsg); +DWORD GetMaxCharWidth(); +int check_buttons(void); + + + +void EnableHeroAppearanceMod(); +void HeroAppearanceModExit(); + +void _stdcall HeroSelectWindow(int RaceStyleFlag); +void _stdcall SetHeroStyle(int newStyleVal); +void _stdcall SetHeroRace(int newRaceVal); +void _stdcall LoadHeroAppearance(void); +void _stdcall SetNewCharAppearanceGlobals(void); + +void _stdcall RefreshPCArt(); diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp new file mode 100644 index 00000000..4ab5ff47 --- /dev/null +++ b/sfall/HookScripts.cpp @@ -0,0 +1,1224 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "HookScripts.h" +#include "ScriptExtender.h" +#include "FalloutEngine.h" +#include "PartyControl.h" +#include "Inventory.h" +//#include "vector9x.cpp" +#include +#include +#include "Logging.h" + +#define MAXDEPTH (8) +static const int numHooks = 25; + +struct sHookScript { + sScriptProgram prog; + int callback; // proc number in script's proc table + bool isGlobalScript; // false for hs_* scripts, true for gl* scripts +}; + +static std::vector hooks[numHooks]; + +DWORD InitingHookScripts; + +static DWORD args[16]; // current hook arguments +static DWORD oldargs[8*MAXDEPTH]; +static DWORD* argPtr; +static DWORD rets[16]; // current hook return values + +static DWORD firstArg=0; +static DWORD callDepth; +static DWORD lastCount[MAXDEPTH]; + +static DWORD ArgCount; +static DWORD cArg; // how many arguments were taken by current hook script +static DWORD cRet; // how many return values were set by current hook script +static DWORD cRetTmp; // how many return values were set by specific hook script (when using register_hook) + +#define hookbegin(a) __asm pushad __asm call BeginHook __asm popad __asm mov ArgCount, a +#define hookend __asm pushad __asm call EndHook __asm popad + +static void _stdcall BeginHook() { + if(callDepth <= MAXDEPTH) { + if(callDepth) { + lastCount[callDepth-1]=ArgCount; + memcpy(&oldargs[8*(callDepth-1)], args, 8*sizeof(DWORD)); + } + argPtr=args; + for(DWORD i=0;icallback != -1) + RunScriptProcByNum(hook->prog.ptr, hook->callback); + else + RunScriptProc(&hook->prog, start_proc); +} +static void _stdcall RunHookScript(DWORD hook) { + if(hooks[hook].size()) { +#ifdef TRACE + char buf[256]; + sprintf_s(buf, "Running hook %d, which has %0d entries attached", hook, hooks[hook].size()); + dlogr(buf, DL_HOOK); +#endif + cRet=0; + for(int i=hooks[hook].size()-1;i>=0;i--) RunSpecificHookScript(&hooks[hook][i]); + } else { + cArg=0; + cRet=0; + } +} + +static const DWORD ToHitAddr=0x4243A8; +static void __declspec(naked) ToHitHook() { + __asm { + hookbegin(4); + mov args[4], eax; + mov args[8], ebx; + mov args[12], ecx; + push [esp+8]; + push [esp+8]; + call ToHitAddr; + mov args[0], eax; + pushad; + push 0; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn 8; + } +} +static const DWORD AfterHitRollAddr=0x423898; +static void __declspec(naked) AfterHitRollHook() { + __asm { + hookbegin(5); + mov args[0], eax; //was it a hit? + mov ebx, [esi]; + mov args[4], ebx; //Attacker + mov ebx, [esi+0x20]; + mov args[8], ebx; //Target + mov ebx, [esi+0x28]; + mov args[12], ebx; //bodypart + mov ebx, [esp+0x18]; + mov args[16], ebx; //hit chance + pushad; + push 1; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; + cmp cRet, 2; + jl end; + mov ebx, rets[4]; + mov [esi+0x28], ebx; + cmp cRet, 3; + jl end; + mov ebx, rets[8]; + mov [esi+0x20], ebx; +end: + mov ebx, eax; + hookend; + cmp ebx, 1; + jmp AfterHitRollAddr; + } +} + +static const DWORD item_w_mp_cost_=0x478B24; +static void __declspec(naked) CalcApCostHook() { + __asm { + hookbegin(4); + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call item_w_mp_cost_; + mov args[12], eax; + pushad; + push 2; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +// this is for using non-weapon items, always 2 AP in vanilla +static void __declspec(naked) CalcApCostHook2() { + __asm { + hookbegin(4); + mov args[0], ecx; // critter + mov args[4], edx; // attack type (to determine hand) + mov args[8], ebx; + mov eax, 2; // vanilla value + mov args[12], eax; + pushad; + push 2; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} + +static const DWORD ObjPidNewAddr=0x489C9C; +static const DWORD ObjEraseObjectAddr=0x48B0FC; +static const DWORD DeathAnimAddr=0x41060C; +static void __declspec(naked) CalcDeathAnimHook() { + __asm { + hookbegin(4); + mov args[24], ebx; + test ebx, ebx; + jz noweap + mov ebx, [ebx+0x64]; + and ebx, 0xfff; + jmp weapend; +noweap: + dec ebx; +weapend: + mov args[0], ebx; + mov ebx, args[24]; + mov args[4], eax; + mov args[8], edx; + mov args[12], ecx; + mov args[20], 0; + pushad; + push 3; + call RunHookScript; + cmp cRet, 1; + jl end1; + sub esp, 4; + mov edx, rets[0]; + mov args[0], edx; + mov eax, esp; + call ObjPidNewAddr; + add esp, 4; + cmp eax, 0xffffffff; + jz end1; + mov eax, [esp-4]; + mov args[20], 1; + mov args[24], eax; +end1: + popad; + mov eax, [esp+8]; + mov ebx, [esp+4]; + push eax; + push ebx; + mov eax, args[4]; + mov ebx, args[24]; + call DeathAnimAddr; + mov args[16], eax; + mov eax, args[16]; + mov ArgCount, 5; + pushad; + push 4; + call RunHookScript; + popad; + cmp cRet, 1; + jl skip2; + mov eax, rets[0]; + mov args[16], eax; +skip2: + mov eax, args[16]; + push eax; + mov eax, args[20]; + test eax, eax; + jz aend; + mov eax, args[24]; + xor edx, edx; + call ObjEraseObjectAddr; +aend: + pop eax; + hookend; + retn 8; + } +} +static const DWORD check_death_=0x410814; +static void __declspec(naked) CalcDeathAnimHook2() { + __asm { + hookbegin(5); + call check_death_; // call original function + mov args[0], -1; // weaponPid, -1 + mov ebx, [esp+60] + mov args[4], ebx; // attacker + mov args[8], esi; // target + mov ebx, [esp+12] + mov args[12], ebx; // dmgAmount + mov args[16], eax; // calculated animID + pushad; + push 4; + call RunHookScript; + popad; + cmp cRet, 1; + jl skip; + mov eax, rets[0]; + mov args[16], eax; +skip: + mov eax, args[16]; + hookend; + retn; + } +} +static const DWORD CombatDamageAddr=0x4247B8; +static void __declspec(naked) CombatDamageHook() { + __asm { + push edx; + push ebx; + push eax; + call CombatDamageAddr; + pop edx; + + //zero damage insta death criticals fix + mov ebx, [edx+0x2c]; + test ebx, ebx; + jnz hookscript; + mov ebx, [edx+0x30]; + test bl, 0x80; + jz hookscript; + inc dword ptr ds:[edx+0x2c]; +hookscript: + hookbegin(11); + mov ebx, [edx+0x20]; + mov args[0x00], ebx; + mov ebx, [edx+0x00]; + mov args[0x04], ebx; + mov ebx, [edx+0x2c]; + mov args[0x08], ebx; + mov ebx, [edx+0x10]; + mov args[0x0c], ebx; + mov ebx, [edx+0x30]; + mov args[0x10], ebx; + mov ebx, [edx+0x14]; + mov args[0x14], ebx; + mov ebx, [edx+0x08]; + mov args[0x18], ebx; + mov ebx, [edx+0x28]; + mov args[0x1c], ebx; + pop ebx; // roll result + mov args[0x20], ebx; + pop ebx; // num rounds + mov args[0x24], ebx; + mov ebx, [edx+0x34]; // knockback value + mov args[0x28], ebx; + + pushad; + push 5; + call RunHookScript; + popad; + + cmp cRet, 1; + jl end; + mov ebx, rets[0x00]; + mov [edx+0x2c], ebx; + cmp cRet, 2; + jl end; + mov ebx, rets[0x04]; + mov [edx+0x10], ebx; + cmp cRet, 3; + jl end; + mov ebx, rets[0x08]; + mov [edx+0x30], ebx; + cmp cRet, 4; + jl end; + mov ebx, rets[0x0c]; + mov [edx+0x14], ebx; + cmp cRet, 5; + jl end; + mov ebx, rets[0x10]; + mov [edx+0x34], ebx; // knockback +end: + hookend; + retn; + } +} +static const DWORD critter_kill=0x42DA64; +static void __declspec(naked) OnDeathHook() { + __asm { + hookbegin(1); + mov args[0], eax; + call critter_kill; + pushad; + push 6; + call RunHookScript; + popad; + hookend; + retn; + } +} +static const DWORD OnDeathHook2Ret=0x4944DC; +static void __declspec(naked) OnDeathHook2() { + __asm { + hookbegin(1); + mov args[0], esi; + call OnDeathHook2Ret; + pushad; + push 6; + call RunHookScript; + popad; + hookend; + retn; + } +} + +static const DWORD _qsort=0x4F05B6; +static void __declspec(naked) FindTargetHook() { + __asm { + hookbegin(5); + mov args[0], esi; //attacker + mov edi, [eax+0]; + mov args[4], edi; + mov edi, [eax+4]; + mov args[8], edi; + mov edi, [eax+8]; + mov args[12], edi; + mov edi, [eax+12]; + mov args[16], edi; + pushad; + push 7 + call RunHookScript; + popad; + cmp cRet, 4; + jge cont; + call _qsort; + jmp end; +cont: + mov edi, rets[0]; + mov [eax+0], edi; + mov edi, rets[4]; + mov [eax+4], edi; + mov edi, rets[8]; + mov [eax+8], edi; + mov edi, rets[12]; + mov [eax+12], edi; +end: + hookend; + retn; + } +} +static const DWORD _protinst_use_item_on=0x49C3CC; +static void __declspec(naked) UseObjOnHook() { + __asm { + hookbegin(3); + mov args[0], edx; // target + mov args[4], eax; // user + mov args[8], ebx; // object + pushad; + push 8; + call RunHookScript; + popad; + cmp cRet, 1; + jl defaulthandler; + mov eax, rets[0]; + jmp end +defaulthandler: + call _protinst_use_item_on; +end: + hookend; + retn; + } +} +static const DWORD item_d_take_drug_=0x479F60; +static void __declspec(naked) UseObjOnHook_item_d_take_drug() { + __asm { + hookbegin(3); + mov args[0], eax; // target + mov args[4], eax; // user + mov args[8], edx; // object + pushad; + push 8; // useobjon + call RunHookScript; + popad; + cmp cRet, 1; + jl defaulthandler; + mov eax, rets[0]; + jmp end +defaulthandler: + call item_d_take_drug_; +end: + hookend; + retn; + } +} +static const DWORD protinst_use_item_=0x49BF38; +static void __declspec(naked) UseObjHook() { + __asm { + hookbegin(2); + mov args[0], eax; // user + mov args[4], edx; // object + pushad; + push 18; + call RunHookScript; + popad; + cmp cRet, 1; + jl defaulthandler; + cmp rets[0], -1; + je defaulthandler; + mov eax, rets[0]; + jmp end; +defaulthandler: + call protinst_use_item_; +end: + hookend; + retn; + } +} +static const DWORD RemoveObjHookRet=0x477497; +static void __declspec(naked) RemoveObjHook() { + __asm { + push ecx; + mov ecx, [esp+4]; + hookbegin(4); + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + mov args[12], ecx; + pushad; + push 9; + call RunHookScript; + popad; + hookend; + push esi; + push edi; + push ebp; + sub esp, 0xc; + jmp RemoveObjHookRet; + } +} +static const DWORD _barter_compute_value=0x474B2C; +static const DWORD _item_caps_total=0x47A6A8; +static const DWORD _item_total_cost=0x477DAC; +static void __declspec(naked) BarterPriceHook() { + __asm { + hookbegin(6); + mov args[0], eax; + mov args[4], edx; + call _barter_compute_value; + mov edx, ds:[0x59E944]; + mov args[8], eax; + mov args[12], edx; + xchg eax, edx; + call _item_caps_total; + mov args[16], eax; + mov eax, ds:[0x59E944]; + call _item_total_cost; + mov args[20], eax; + mov eax, edx; + pushad; + push 10; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +static const DWORD _critter_compute_ap_from_distance=0x42E62C; +static void __declspec(naked) MoveCostHook() { + __asm { + hookbegin(3); + mov args[0], eax; + mov args[4], edx; + call _critter_compute_ap_from_distance; + mov args[8], eax; + pushad; + push 11; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +static const DWORD _obj_blocking_at=0x48B84E; +static void __declspec(naked) HexMBlockingHook() { + __asm { + hookbegin(4); + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + push next; + push ecx; + push esi; + push edi; + push ebp; + mov ecx, eax; + jmp _obj_blocking_at; +next: + mov args[12], eax; + pushad; + push 12; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +static const DWORD _obj_ai_blocking_at=0x48BA20; +static void __declspec(naked) HexABlockingHook() { + __asm { + hookbegin(4); + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call _obj_ai_blocking_at; + mov args[12], eax; + pushad; + push 13; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +static const DWORD _obj_shoot_blocking_at=0x48B930; +static void __declspec(naked) HexShootBlockingHook() { + __asm { + hookbegin(4); + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call _obj_shoot_blocking_at; + mov args[12], eax; + pushad; + push 14; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +static const DWORD _obj_sight_blocking_at=0x48BB88; +static void __declspec(naked) HexSightBlockingHook() { + __asm { + hookbegin(4); + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call _obj_sight_blocking_at; + mov args[12], eax; + pushad; + push 15; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} +static const DWORD _roll_random=0x4A30C0; +static void __declspec(naked) ItemDamageHook() { + __asm { + hookbegin(6); + mov args[0], eax; //min + mov args[4], edx; //max + mov args[8], edi; //weapon + mov args[12], ecx; //critter + mov args[16], esi; //type + mov args[20], ebp; //non-zero for weapon melee attack + test edi, edi; + jnz skip; + add args[16], 8; +skip: + pushad; + push 16; + call RunHookScript; + popad; + cmp cRet, 0; + je runrandom; + mov eax, rets[0]; + cmp cRet, 1; + je end; + mov edx, rets[4]; +runrandom: + call _roll_random; +end: + hookend; + retn; + } +} +static const DWORD item_w_compute_ammo_cost_ = 0x4790AC; // signed int aWeapon, int *aRoundsSpent + +static void __declspec(naked) AmmoCostHook_internal() { + __asm { + pushad; + mov args[0], eax; //weapon + mov ebx, [edx] + mov args[4], ebx; //rounds in attack + call item_w_compute_ammo_cost_; + cmp eax, -1 + je fail + mov ebx, [edx] + mov args[8], ebx; //rounds as computed by game + + push 17; + call RunHookScript; + popad; + cmp cRet, 0; + je end; + mov eax, rets[0] + mov [edx], eax; // override result + mov eax, 0 + jmp end +fail: + popad +end: + hookend; + retn; + } +} +static void __declspec(naked) AmmoCostHook() { + __asm { + hookbegin(4); + mov args[12], 0 // type of hook + jmp AmmoCostHook_internal; + } +} +void __declspec(naked) AmmoCostHookWrapper() { + __asm { + hookbegin(4); + push eax; + mov eax, [esp+8]; // hook type + mov args[12], eax; + pop eax; + call AmmoCostHook_internal; + retn; + } +} +void _stdcall KeyPressHook( DWORD dxKey, bool pressed, DWORD vKey ) +{ + BeginHook(); + ArgCount = 3; + args[0] = (DWORD)pressed; + args[1] = dxKey; + args[2] = vKey; + RunHookScript(19); + InventoryKeyPressedHook(dxKey, pressed, vKey); + EndHook(); +} +void _stdcall MouseClickHook(DWORD button, bool pressed) { + BeginHook(); + ArgCount = 2; + args[0] = (DWORD)pressed; + args[1] = button; + RunHookScript(20); + EndHook(); +} + +static const DWORD skill_use_=0x4AAD08; +static void __declspec(naked) UseSkillHook() { + __asm { + hookbegin(4); + mov args[0], eax; // user + mov args[4], edx; // target + mov args[8], ebx; // skill id + mov args[12], ecx; // skill bonus + pushad; + push 21; + call RunHookScript; + popad; + cmp cRet, 1; + jl defaulthandler; + cmp rets[0], -1; + je defaulthandler; + mov eax, rets[0]; + jmp end +defaulthandler: + call skill_use_; +end: + hookend; + retn; + } +} + +static const DWORD skill_check_stealing_=0x4ABBE4; +static void __declspec(naked) StealCheckHook() { + __asm { + hookbegin(4); + mov args[0], eax; // thief + mov args[4], edx; // target + mov args[8], ebx; // item + mov args[12], ecx; // is planting + pushad; + push 22; + call RunHookScript; + popad; + cmp cRet, 1; + jl defaulthandler; + cmp rets[0], -1; + je defaulthandler; + mov eax, rets[0]; + jmp end +defaulthandler: + call skill_check_stealing_; +end: + hookend; + retn; + } +} + +static const DWORD is_within_perception_=0x42BA04; +static void __declspec(naked) PerceptionRangeHook() { + __asm { + hookbegin(3); + mov args[0], eax; // watcher + mov args[4], edx; // target + call is_within_perception_; + mov args[8], eax; // check result + pushad; + push 23; + call RunHookScript; + popad; + cmp cRet, 1; + jl end; + mov eax, rets[0]; +end: + hookend; + retn; + } +} + +// jmp here, not call +static const DWORD PerceptionRangeBonusHack_back = 0x456BA7; +static const DWORD PerceptionRangeBonusHack_skip_blocking_check = 0x456BDC; +static void __declspec(naked) PerceptionRangeBonusHack() { + __asm { + call PerceptionRangeHook; + cmp eax, 2; + jne nevermind; + mov dword ptr [esp+16], 1; + jmp PerceptionRangeBonusHack_skip_blocking_check; +nevermind: + jmp PerceptionRangeBonusHack_back; + } +} + +static int __stdcall SwitchHandHook2(TGameObj* item, TGameObj* itemReplaced, DWORD addr) { + int tmp; + if (itemReplaced && ItemGetType(itemReplaced) == 3 && ItemGetType(item) == 4) { + return -1; // to prevent inappropriate hook call after dropping ammo on weapon + } + BeginHook(); + ArgCount = 3; + args[0] = (addr < 0x47136D) ? 1 : 2; + args[1] = (DWORD)item; + args[2] = (DWORD)itemReplaced; + RunHookScript(24); // moveinventory + tmp = PartyControl_SwitchHandHook(item); + if (tmp != -1) { + cRetTmp = 0; + SetHSReturn(tmp); + } + EndHook(); + if (cRet > 0) + return rets[0]; + return -1; +} + +/* + This hook is called every time an item is placed into either hand slot via inventory screen drag&drop + If switch_hand_ function is not called, item is not placed anywhere (it remains in main inventory) +*/ +static const DWORD switch_hand_ = 0x4714E0; +static void _declspec(naked) SwitchHandHook() { + _asm { + pushad; + mov ecx, eax; + mov eax, [esp+32]; // back address + push eax; + mov edx, [edx]; + push edx; // other item + push ecx; // item being moved + call SwitchHandHook2; + cmp eax, -1; + popad; + jne skip; + call switch_hand_; +skip: + retn; + } +} + +static const DWORD UseArmorHack_back = 0x4713A9; // normal operation +static const DWORD UseArmorHack_skip = 0x471481; // skip code, prevent wearing armor +static const DWORD* i_worn = (DWORD*)0x59E954; // item in armor slot +// This hack is called when an armor is dropped into the armor slot at inventory screen +static void _declspec(naked) UseArmorHack() { + __asm { + cmp eax, 0; + jne skip; // not armor + hookbegin(3); + mov args[0], 3; + mov eax, [esp+24]; // item + mov args[4], eax; + mov eax, i_worn; + mov eax, [eax]; + mov args[8], eax; + pushad; + push 24; + call RunHookScript; + popad; + cmp cRet, 1; + jl back; + cmp rets[0], -1; + jne skip; +back: + hookend; + jmp UseArmorHack_back; +skip: + hookend; + jmp UseArmorHack_skip; + } +} + +//static const DWORD drop_into_container_ = 0x476464; +static const DWORD item_add_force_ = 0x4772B8; +static void _declspec(naked) MoveInventoryHook() { + __asm { + hookbegin(3); + mov args[0], 0; + mov args[4], edx; + mov args[8], 0; // no item being replaced here.. + pushad; + push 24; + call RunHookScript; + popad; + cmp cRet, 1; + jl skipcheck; + cmp rets[0], -1; + jne skipcall; +skipcheck: + call item_add_force_; +skipcall: + hookend; + retn; + } +} + +//static const DWORD drop_ammo_into_weapon_ = 0x47650C; +static const DWORD DropAmmoIntoWeaponHack_back = 0x47658D; // proceed with reloading +static const DWORD DropAmmoIntoWeaponHack_return = 0x476643; +static const DWORD item_w_can_reload_ = 0x478874; +static void _declspec(naked) DropAmmoIntoWeaponHack() { + __asm { + hookbegin(3); + mov args[0], 4; + mov eax, [esp]; + mov args[4], eax; + mov args[8], ebp; + pushad; + push 24; + call RunHookScript; + popad; + cmp cRet, 1; + jl proceedreloading; + cmp rets[0], -1; + jne donothing; +proceedreloading: + hookend; + mov ebx, 1; // overwritten code + jmp DropAmmoIntoWeaponHack_back; +donothing: + hookend; + mov eax, 0; + jmp DropAmmoIntoWeaponHack_return; + } +} + +DWORD _stdcall GetHSArgCount() { + return ArgCount; +} +DWORD _stdcall GetHSArg() { + if(cArg==ArgCount) return 0; + else return args[cArg++]; +} +void _stdcall SetHSArg(DWORD id, DWORD value) { + if(id cRet) + cRet = cRetTmp; +} +void _stdcall RegisterHook( DWORD script, DWORD id, DWORD procNum ) +{ + if (id >= numHooks) return; + for (std::vector::iterator it = hooks[id].begin(); it != hooks[id].end(); ++it) { + if (it->prog.ptr == script) { + if (procNum == 0) hooks[id].erase(it); // unregister + return; + } + } + sScriptProgram *prog = GetGlobalScriptProgram(script); + if (prog) { +#ifdef TRACE + dlog_f( "Global script %8x registered as hook id %d ", DL_HOOK, script, id); +#endif + sHookScript hook; + hook.prog = *prog; + hook.callback = procNum; + hook.isGlobalScript = true; + hooks[id].push_back(hook); + } +} +#define LoadHookScript(a,b) _LoadHookScript("data\\scripts\\hs_" a ".int", b) +static void _LoadHookScript(const char* path, int id) { + if(id>=numHooks) return; + WIN32_FIND_DATA file; + HANDLE h; + + h = FindFirstFileA(path, &file); + if(h != INVALID_HANDLE_VALUE) { + sScriptProgram prog; + dlog("Loading hook script: ", DL_HOOK); + dlogr(path, DL_HOOK); + char* fName = file.cFileName; + fName[strlen(fName) - 4] = 0; + LoadScriptProgram(prog, fName); + FindClose(h); + if (prog.ptr) { + sHookScript hook; + hook.prog = prog; + hook.callback = -1; + hook.isGlobalScript = false; + hooks[id].push_back(hook); + AddProgramToMap(prog); + } + } +} +static void HookScriptInit2() { + dlogr("Initing hook scripts", DL_HOOK|DL_INIT); + + LoadHookScript("tohit", 0); + HookCall(0x421686, &ToHitHook); + HookCall(0x4231D9, &ToHitHook); + HookCall(0x42331F, &ToHitHook); + HookCall(0x4237FC, &ToHitHook); + HookCall(0x424379, &ToHitHook); + HookCall(0x42438D, &ToHitHook); + HookCall(0x42439C, &ToHitHook); + HookCall(0x42679A, &ToHitHook); + + LoadHookScript("afterhitroll", 1); + MakeCall(0x423893, &AfterHitRollHook, true); + + LoadHookScript("calcapcost", 2); + HookCall(0x42307A, &CalcApCostHook); + HookCall(0x42669F, &CalcApCostHook); + HookCall(0x42687B, &CalcApCostHook); + HookCall(0x42A625, &CalcApCostHook); + HookCall(0x42A655, &CalcApCostHook); + HookCall(0x42A686, &CalcApCostHook); + HookCall(0x42AE32, &CalcApCostHook); + HookCall(0x42AE71, &CalcApCostHook); + HookCall(0x460048, &CalcApCostHook); + HookCall(0x47807B, &CalcApCostHook); + MakeCall(0x478083, &CalcApCostHook2, false); + + LoadHookScript("deathanim1", 3); + LoadHookScript("deathanim2", 4); + HookCall(0x4109DE, &CalcDeathAnimHook); + HookCall(0x410981, &CalcDeathAnimHook2); + HookCall(0x4109A1, &CalcDeathAnimHook2); + HookCall(0x4109BF, &CalcDeathAnimHook2); + + LoadHookScript("combatdamage", 5); + HookCall(0x42326C, &CombatDamageHook); // check_ranged_miss() + HookCall(0x4233E3, &CombatDamageHook); // shoot_along_path() - for extra burst targets + HookCall(0x423AB7, &CombatDamageHook); // compute_attack() + HookCall(0x423BBF, &CombatDamageHook); // compute_attack() + HookCall(0x423DE7, &CombatDamageHook); // compute_explosion_on_extras() + HookCall(0x423E69, &CombatDamageHook); // compute_explosion_on_extras() + HookCall(0x424220, &CombatDamageHook); // attack_crit_failure() + HookCall(0x4242FB, &CombatDamageHook); // attack_crit_failure() + + LoadHookScript("ondeath", 6); + HookCall(0x4130CC, &OnDeathHook); + HookCall(0x4130EF, &OnDeathHook); + HookCall(0x413603, &OnDeathHook); + HookCall(0x426EF0, &OnDeathHook); + HookCall(0x42D1EC, &OnDeathHook); + HookCall(0x42D6F9, &OnDeathHook); + HookCall(0x457BC5, &OnDeathHook); + HookCall(0x457E3A, &OnDeathHook); + HookCall(0x457E54, &OnDeathHook); + HookCall(0x4C14F9, &OnDeathHook); + HookCall(0x425161, &OnDeathHook2); + + LoadHookScript("findtarget", 7); + HookCall(0x429143, &FindTargetHook); + + LoadHookScript("useobjon", 8); + HookCall(0x49C606, &UseObjOnHook); + HookCall(0x473619, &UseObjOnHook); + // the following hooks allows to catch drug use of AI and from action cursor + HookCall(0x4285DF, &UseObjOnHook_item_d_take_drug); // ai_check_drugs + HookCall(0x4286F8, &UseObjOnHook_item_d_take_drug); // ai_check_drugs + HookCall(0x4287F8, &UseObjOnHook_item_d_take_drug); // ai_check_drugs + HookCall(0x473573, &UseObjOnHook_item_d_take_drug); // inven_action_cursor + + LoadHookScript("removeinvenobj", 9); + MakeCall(0x477490, &RemoveObjHook, true); + + LoadHookScript("barterprice", 10); + HookCall(0x474D4C, &BarterPriceHook); + HookCall(0x475735, &BarterPriceHook); + HookCall(0x475762, &BarterPriceHook); + + LoadHookScript("movecost", 11); + HookCall(0x417665, &MoveCostHook); + HookCall(0x44B88A, &MoveCostHook); + + LoadHookScript("hexmoveblocking", 12); + LoadHookScript("hexaiblocking", 13); + LoadHookScript("hexshootblocking", 14); + LoadHookScript("hexsightblocking", 15); + SafeWrite32(0x413979, (DWORD)&HexSightBlockingHook); + SafeWrite32(0x4C1A88, (DWORD)&HexShootBlockingHook); + SafeWrite32(0x423178, (DWORD)&HexShootBlockingHook); + SafeWrite32(0x4232D4, (DWORD)&HexShootBlockingHook); + SafeWrite32(0x423B4D, (DWORD)&HexShootBlockingHook); + SafeWrite32(0x426CF8, (DWORD)&HexShootBlockingHook); + SafeWrite32(0x42A570, (DWORD)&HexShootBlockingHook); + SafeWrite32(0x42A0A4, (DWORD)&HexABlockingHook); + MakeCall(0x48B848, &HexMBlockingHook, true); + + LoadHookScript("itemdamage", 16); + HookCall(0x478560, &ItemDamageHook); + + LoadHookScript("ammocost", 17); + HookCall(0x423A7C, &AmmoCostHook); + + LoadHookScript("useobj", 18); + HookCall(0x42AEBF, &UseObjHook); + HookCall(0x473607, &UseObjHook); + HookCall(0x49C12E, &UseObjHook); + + LoadHookScript("keypress", 19); + LoadHookScript("mouseclick", 20); + + LoadHookScript("useskill", 21); + HookCall(0x49C48F, &UseSkillHook); + HookCall(0x49D12E, &UseSkillHook); + + LoadHookScript("steal", 22); + HookCall(0x4749A2, &StealCheckHook); + HookCall(0x474A69, &StealCheckHook); + + LoadHookScript("withinperception", 23); + HookCall(0x429157, &PerceptionRangeHook); + HookCall(0x42B4ED, &PerceptionRangeHook); + HookCall(0x42BC87, &PerceptionRangeHook); + HookCall(0x42BC9F, &PerceptionRangeHook); + HookCall(0x42BD04, &PerceptionRangeHook); + MakeCall(0x456BA2, &PerceptionRangeBonusHack, true); + HookCall(0x458403, &PerceptionRangeHook); + + LoadHookScript("inventorymove", 24); + HookCall(0x4712E3, &SwitchHandHook); // left slot + HookCall(0x47136D, &SwitchHandHook); // right slot + MakeCall(0x4713A3, &UseArmorHack, true); + //HookCall(0x4711B3, &DropIntoContainerHook); + //HookCall(0x47147C, &DropIntoContainerHook); + HookCall(0x471200, &MoveInventoryHook); + //HookCall(0x4712C7, &DropAmmoIntoWeaponHook); + //HookCall(0x471351, &DropAmmoIntoWeaponHook); + MakeCall(0x476588, &DropAmmoIntoWeaponHack, true); + + dlogr("Completed hook script init", DL_HOOK|DL_INIT); + +} + +void HookScriptClear() { + for(int i = 0; i < numHooks; i++) { + hooks[i].clear(); + } +} + +void HookScriptInit() { + isGlobalScriptLoading = 1; // this should allow to register global exported variables + HookScriptInit2(); + InitingHookScripts = 1; + for (int i = 0; i < numHooks; i++) { + if (hooks[i].size()) { + InitScriptProgram(hooks[i][0].prog);// zero hook is always hs_*.int script because Hook scripts are loaded BEFORE global scripts + } + } + isGlobalScriptLoading = 0; + InitingHookScripts = 0; +} + +// run specific event procedure for all hook scripts +void _stdcall RunHookScriptsAtProc(DWORD procId) { + for (int i = 0; i < numHooks; i++) { + if (hooks[i].size() > 0 && !hooks[i][0].isGlobalScript) { + RunScriptProc(&hooks[i][0].prog, procId); + } + } +} + diff --git a/sfall/HookScripts.h b/sfall/HookScripts.h new file mode 100644 index 00000000..63345fd5 --- /dev/null +++ b/sfall/HookScripts.h @@ -0,0 +1,36 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +DWORD _stdcall GetHSArgCount(); +DWORD _stdcall GetHSArg(); +DWORD* _stdcall GetHSArgs(); +void _stdcall SetHSArg(DWORD id, DWORD value); +void _stdcall SetHSReturn(DWORD d); +// register hook by proc num (special values: -1 - use default (start) procedure, 0 - unregister) +void _stdcall RegisterHook(DWORD script, DWORD id, DWORD procNum); + +void HookScriptInit(); +void HookScriptClear(); + +extern DWORD InitingHookScripts; +extern void __declspec() AmmoCostHookWrapper(); +void _stdcall MouseClickHook(DWORD button, bool pressed); +void _stdcall KeyPressHook(DWORD dxKey, bool pressed, DWORD vKey); +void _stdcall RunHookScriptsAtProc(DWORD procId); \ No newline at end of file diff --git a/sfall/Imports.cpp b/sfall/Imports.cpp new file mode 100644 index 00000000..459dcc0c --- /dev/null +++ b/sfall/Imports.cpp @@ -0,0 +1,228 @@ +#include "Imports.h" + +int db_fclose(sFile* file) { + DWORD addr=0x4C5EB4; + int result; + _asm { + mov eax, file; + call addr; + mov result, eax; + } + return result; +} +sFile* db_fopen(const char* path, const char* mode) { + DWORD addr=0x4C5EC8; + sFile* result; + _asm { + mov eax, path; + mov edx, mode; + call addr; + mov result, eax; + } + return result; +} +int db_fgetc(sFile* file) { + DWORD addr=0x4C5F24; + int result; + _asm { + mov eax, file; + call addr; + mov result, eax; + } + return result; +} +char* db_fgets(char* buf, int max_count, sFile* file) { + DWORD addr=0x4C5F70; + char* result; + _asm { + mov eax, buf; + mov edx, max_count; + mov ebx, file; + call addr; + mov result, eax; + } + return result; +} +//int db_ungetc(int c, sFile* file); +int db_fread(void* buf, int elsize, int count, sFile* file) { + DWORD addr=0x4C5FFC; + int result; + _asm { + mov eax, buf; + mov edx, elsize; + mov ebx, count; + mov ecx, file; + call addr; + mov result, eax; + } + return result; +} +int db_fseek(sFile* file, long pos, int origin) { + DWORD addr=0x4C60C0; + int result; + _asm { + mov eax, file; + mov edx, pos; + mov ebx, origin; + call addr; + mov result, eax; + } + return result; +} +//long db_ftell(sFile* file); +//void db_rewind(sFile* file); +//int db_feof(sFile* file); +int db_freadByte(sFile* file, __int8* _out) { + DWORD addr=0x4C60E0; + int result; + _asm { + mov eax, file; + mov edx, _out; + call addr; + mov result, eax; + } + return result; +} +int db_freadShort(sFile* file, __int16* _out) { + DWORD addr=0x4C60F4; + int result; + _asm { + mov eax, file; + mov edx, _out; + call addr; + mov result, eax; + } + return result; +} +int db_freadInt(sFile* file, __int32* _out) { + DWORD addr=0x4C614C; + int result; + _asm { + mov eax, file; + mov edx, _out; + call addr; + mov result, eax; + } + return result; +} +int db_freadFloat(sFile* file, float* _out) { + DWORD addr=0x4C614C; + int result; + _asm { + mov eax, file; + mov edx, _out; + call addr; + mov result, eax; + } + return result; +} +//int db_freadByteCount(sFile* file, BYTE* ptr, int count); +//int db_freadShortCount(sFile* file, WORD* ptr, int count); +//int db_freadIntCount(sFile* file, DWORD* ptr, int count); +//int db_freadFloatCount(sFile* file, float* ptr, int count); + +int message_init(sMsgFile* file) { + DWORD addr=0x48494C; + int result; + _asm { + mov eax, file; + call addr; + mov result, eax; + } + return result; +} +int message_exit(sMsgFile* file) { + DWORD addr=0x484964; + int result; + _asm { + mov eax, file; + call addr; + mov result, eax; + } + return result; +} +int message_load(sMsgFile* file, char* path) { + DWORD addr=0x484AA4; + int result; + _asm { + mov eax, file; + mov edx, path + call addr; + mov result, eax; + } + return result; +} +int message_search(sMsgFile* file, sMessage* msg) { + DWORD addr=0x484C30; + int result; + _asm { + mov eax, file; + mov edx, msg; + call addr; + mov result, eax; + } + return result; +} +int message_make_path(char* outpath, char* path) { + DWORD addr=0x484CB8; + int result; + _asm { + mov eax, outpath; + mov edx, path; + call addr; + mov result, eax; + } + return result; +} +int message_find(sMsgFile* file, DWORD number, DWORD* _out) { + DWORD addr=0x484D10; + int result; + _asm { + mov eax, file; + mov edx, number; + mov ebx, _out; + call addr; + mov result, eax; + } + return result; +} +int message_add(sMsgFile* file, sMessage* msg) { + DWORD addr=0x484D68; + int result; + _asm { + mov eax, file; + mov edx, msg; + call addr; + mov result, eax; + } + return result; +} +char* getmsg(sMsgFile* file, sMessage* msg, DWORD num) { + DWORD addr=0x48504C; + char* result; + _asm { + mov eax, file; + mov edx, msg; + mov ebx, num; + call addr; + mov result, eax; + } + return result; +} +int message_filter(sMsgFile* file) { + DWORD addr=0x485078; + int result; + _asm { + mov eax, file; + call addr; + mov result, eax; + } + return result; +} + +void get_input() { + DWORD addr=0x4C8B78; + _asm { + call addr; + } +} \ No newline at end of file diff --git a/sfall/Imports.h b/sfall/Imports.h new file mode 100644 index 00000000..74c2eb04 --- /dev/null +++ b/sfall/Imports.h @@ -0,0 +1,48 @@ +#define WIN32_LEAN_AND_MEAN +#include + +struct sFile { + DWORD fileType; + void* handle; +}; +struct sMessage { + int number; + DWORD flags; + char* audio; + char* message; +}; +struct sMsgFile { + DWORD count; + sMessage* messages; +}; + +int db_fclose(sFile* file); +sFile* db_fopen(const char* path, const char* mode); +int db_fgetc(sFile* file); +char* db_fgets(char* buf, int max_count, sFile* file); +int db_ungetc(int c, sFile* file); +int db_fread(void* buf, int elsize, int count, sFile* file); +int db_fseek(sFile* file, long pos, int origin); +long db_ftell(sFile* file); +void db_rewind(sFile* file); +int db_feof(sFile* file); +int db_freadByte(sFile* file, __int8* out); +int db_freadShort(sFile* file, __int16* out); +int db_freadInt(sFile* file, __int32* out); +int db_freadFloat(sFile* file, float* out); +int db_freadByteCount(sFile* file, BYTE* ptr, int count); +int db_freadShortCount(sFile* file, WORD* ptr, int count); +int db_freadIntCount(sFile* file, DWORD* ptr, int count); +int db_freadFloatCount(sFile* file, float* ptr, int count); + +int message_init(sMsgFile* file); +int message_exit(sMsgFile* file); +int message_load(sMsgFile* file, char* path); +int message_search(sMsgFile* file, sMessage* msg); +int message_make_path(char* outpath, char* path); +int message_find(sMsgFile* file, DWORD number, DWORD* out); +int message_add(sMsgFile* file, sMessage* msg); +char* getmsg(sMsgFile* file, sMessage* msg, DWORD num); +int message_filter(sMsgFile* file); + +void get_input(); \ No newline at end of file diff --git a/sfall/Inventory.cpp b/sfall/Inventory.cpp new file mode 100644 index 00000000..cefba3ba --- /dev/null +++ b/sfall/Inventory.cpp @@ -0,0 +1,618 @@ +/* + * sfall + * Copyright (C) 2011 Timeslip + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" +#include "Inventory.h" +#include "HookScripts.h" +#include "FalloutEngine.h" +#include "LoadGameHook.h" + +#include + +static DWORD mode; +static DWORD MaxItemSize; +static DWORD ReloadWeaponKey = 0; + +struct sMessage { + DWORD number; + DWORD flags; + char* audio; + char* message; +}; +static const DWORD message_search=0x484C30; +static const char* MsgSearch(int msgno, DWORD file) { + if(!file) return 0; + sMessage msg = { msgno, 0, 0, 0 }; + __asm { + lea edx, msg; + mov eax, file; + call message_search; + } + return msg.message; +} + +DWORD& GetActiveItemMode() { + return itemButtonItems[(*itemCurrentItem * 6) + 4]; +} + +TGameObj* GetActiveItem() { + return (TGameObj*)itemButtonItems[*itemCurrentItem * 6]; +} + +void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey) { + if (pressed && ReloadWeaponKey && dxKey == ReloadWeaponKey && (GetCurrentLoops() & ~(COMBAT | PCOMBAT)) == 0) { + DWORD maxAmmo, curAmmo; + TGameObj* item = GetActiveItem(); + __asm { + mov eax, item; + call item_w_max_ammo_; + mov maxAmmo, eax; + mov eax, item; + call item_w_cur_ammo_; + mov curAmmo, eax; + } + if (maxAmmo != curAmmo) { + DWORD ¤tMode = GetActiveItemMode(); + DWORD previusMode = currentMode; + currentMode = 5; // reload mode + __asm { + call intface_use_item_; + } + if (previusMode != 5) { + // return to previous active item mode (if it wasn't "reload") + currentMode = previusMode - 1; + if (currentMode < 0) + currentMode = 4; + __asm { + call intface_toggle_item_state_; + } + } + } + } +} + +static const DWORD item_total_weight=0x477E98; +static const DWORD item_size=0x477B68; + +/*static DWORD _stdcall item_total_size(void* critter) { + //TODO: Don't really want to be overwriting stuff like this after init. Rewrite properly. + HookCall(0x477EBD, (void*)0x477B68); + HookCall(0x477EF6, (void*)0x477B68); + HookCall(0x477F12, (void*)0x477B68); + HookCall(0x477F2A, (void*)0x477B68); + + DWORD result; + __asm { + mov eax, critter; + call item_total_weight; + mov result, eax; + } + + HookCall(0x477EBD, (void*)0x477B88); + HookCall(0x477EF6, (void*)0x477B88); + HookCall(0x477F12, (void*)0x477B88); + HookCall(0x477F2A, (void*)0x477B88); + + return result; +}*/ + +static const DWORD inven_right_hand=0x471B70; +static const DWORD inven_left_hand=0x471BBC; +static const DWORD inven_worn=0x471C08; //TODO: Do we actually want to include this in the limit anyway? +static __declspec(naked) DWORD item_total_size(void* critter) { + __asm { + push ebx; + push ecx; + push edx; + push esi; + push edi; + push ebp; + mov ebp, eax; + test eax, eax; + jz loc_477F33; + lea edi, [eax+2Ch]; + xor edx, edx; + mov ebx, [edi]; + xor esi, esi; + test ebx, ebx; + jle loc_477ED3; + xor ebx, ebx; +loc_477EB7: + mov ecx, [edi+8]; + mov eax, [ecx+ebx]; + call item_size; + imul eax, [ecx+ebx+4]; + add ebx, 8; + inc edx; + mov ecx, [edi]; + add esi, eax; + cmp edx, ecx; + jl loc_477EB7; +loc_477ED3: + mov eax, [ebp+20h]; + and eax, 0F000000h; + sar eax, 18h; + cmp eax, 1; + jnz loc_477F31; + mov eax, ebp; + call inven_right_hand; + mov edx, eax; + test eax, eax; + jz loc_477EFD; + test byte ptr [eax+27h], 2; + jnz loc_477EFD; + call item_size; + add esi, eax; +loc_477EFD: + mov eax, ebp; + call inven_left_hand; + test eax, eax; + jz loc_477F19; + cmp edx, eax; + jz loc_477F19; + test byte ptr [eax+27h], 1; + jnz loc_477F19; + call item_size; + add esi, eax; +loc_477F19: + mov eax, ebp; + call inven_worn; + test eax, eax; + jz loc_477F31; + test byte ptr [eax+27h], 4; + jnz loc_477F31; + call item_size; + add esi, eax; +loc_477F31: + mov eax, esi; +loc_477F33: + pop ebp; + pop edi; + pop esi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +/*static const DWORD ObjPickupFail=0x49B70D; +static const DWORD ObjPickupEnd=0x49B6F8; +static const DWORD size_limit; +static __declspec(naked) void ObjPickupHook() { + __asm { + cmp edi, [0x6610B8]; + jnz end; +end: + lea edx, [esp+0x10]; + mov eax, ecx; + jmp ObjPickupEnd; + } +}*/ + +static __declspec(naked) int CritterCheck() { + __asm { + push ebx; + push edx; + sub esp, 4; + mov ebx, eax; + + cmp eax, dword ptr ds:[0x6610B8]; + je single; + test mode, 3; + jnz run; + test mode, 2; + jz fail; + call isPartyMember_; + test eax, eax; + jz end; +run: + test mode, 8; + jz single; + mov edx, esp; + mov eax, ebx; + call proto_ptr_; + mov eax, [esp]; + mov eax, [eax + 0xB0 + 40]; //The unused stat in the extra block + jmp end; +single: + mov eax, MaxItemSize; + jmp end; +fail: + xor eax, eax; +end: + add esp, 4; + pop edx; + pop ebx; + retn; + } +} + +static const DWORD IsOverloadedEnd=0x42E68D; +static __declspec(naked) void CritterIsOverloadedHook() { + __asm { + and eax, 0xff; + jnz end; + mov eax, ebx; + call CritterCheck; + test eax, eax; + jz end; + xchg eax, ebx; + call item_total_size; + cmp eax, ebx; + setg al; + and eax, 0xff; +end: + jmp IsOverloadedEnd; + } +} + +static const DWORD ItemAddMultiRet=0x4772A6; +static const DWORD ItemAddMultiFail=0x4771C7; +static __declspec(naked) void ItemAddMultiHook1() { + __asm { + push ebp; + mov eax, ecx; + call CritterCheck; + test eax, eax; + jz end; + mov ebp, eax; + mov eax, esi; + call item_size; + mov edx, eax; + imul edx, ebx; + mov eax, ecx; + call item_total_size; + add edx, eax; + cmp edx, ebp; + jle end; + mov eax, -6; //TODO: Switch this to a lower number, and add custom error messages. + pop ebp; + jmp ItemAddMultiFail; +end: + pop ebp; + jmp ItemAddMultiRet; + } +} + +static __declspec(naked) void ItemAddMultiHook2() { + __asm { + cmp eax, edi; + jl fail; + jmp ItemAddMultiHook1; +fail: + mov eax, -6; + jmp ItemAddMultiFail; + } +} + +static const DWORD BarterAttemptTransactionHook1Fail=0x474C81; +static const DWORD BarterAttemptTransactionHook1End=0x474CA8; +static __declspec(naked) void BarterAttemptTransactionHook1() { + __asm { + cmp eax, edx; + jg fail; + mov eax, edi; + call CritterCheck; + test eax, eax; + jz end; + mov edx, eax; + mov eax, edi; + call item_total_size; + sub edx, eax; + mov eax, ebp; + call item_total_size; + cmp eax, edx; + jle end; +fail: + mov esi, 0x1f; + jmp BarterAttemptTransactionHook1Fail; +end: + jmp BarterAttemptTransactionHook1End; + } +} + +static const DWORD BarterAttemptTransactionHook2Fail=0x474CD8; +static const DWORD BarterAttemptTransactionHook2End=0x474D01; +static __declspec(naked) void BarterAttemptTransactionHook2() { + __asm { + cmp eax, edx; + jg fail; + mov eax, ebx; + call CritterCheck; + test eax, eax; + jz end; + mov edx, eax; + mov eax, ebx; + call item_total_size; + sub edx, eax; + mov eax, esi; + call item_total_size; + cmp eax, edx; + jle end; +fail: + mov ecx, 0x20; + jmp BarterAttemptTransactionHook2Fail; +end: + jmp BarterAttemptTransactionHook2End; + } +} + +static char SizeStr[16]; +static char InvenFmt[32]; +static const char* InvenFmt1="%s %d/%d %s %d/%d"; +static const char* InvenFmt2="%s %d/%d"; + +static const char* _stdcall GetInvenMsg() { + const char* tmp=MsgSearch(35, 0x59E814); + if(!tmp) return "S:"; + else return tmp; +} + +static void _stdcall strcpy_wrapper(char* buf, const char* str) { + strcpy(buf, str); +} + +static const DWORD DisplayStatsEnd=0x4725E5; +static __declspec(naked) void DisplayStatsHook() { + __asm { + call CritterCheck; + jz nolimit; + push eax; + mov eax, ds:[0x59E86C]; + push ecx; + push InvenFmt1; + push offset InvenFmt; + call strcpy_wrapper; + pop ecx; + mov eax, ds:[0x59E86C]; + call item_total_size; + push eax; + push ecx; + call GetInvenMsg; + pop ecx; + push eax; + jmp end; +nolimit: + push ecx; + push InvenFmt2; + push offset InvenFmt; + call strcpy_wrapper; + pop ecx; + push eax; + push eax; + push eax; +end: + mov eax, ds:[0x59E86C]; + mov edx, 0xc; + jmp DisplayStatsEnd; + } +} + +static char SizeMsgBuf[32]; +static const char* _stdcall FmtSizeMsg(int size) { + if(size==1) { + const char* tmp=MsgSearch(543, 0x6647FC); + if(!tmp) strcpy(SizeMsgBuf, "It occupies 1 unit."); + else sprintf(SizeMsgBuf, tmp, size); + } else { + const char* tmp=MsgSearch(542, 0x6647FC); + if(!tmp) sprintf(SizeMsgBuf, "It occupies %d units.", size); + else sprintf(SizeMsgBuf, tmp, size); + } + return SizeMsgBuf; +} + +static const DWORD InvenDisplayMsg=0x472D24; +static __declspec(naked) void InvenObjExamineFuncHook() { + __asm { + call InvenDisplayMsg; + push edx; + push ecx; + mov eax, esi; + call item_size; + push eax; + call FmtSizeMsg; + pop ecx; + pop edx; + call InvenDisplayMsg; + retn; + } +} + +static char SuperStimMsg[128]; +static int _stdcall SuperStimFix2(DWORD* item, DWORD* target) { + if(!item || !target) return 0; + DWORD itm_pid=item[0x64/4], target_pid=target[0x64/4]; + if((target_pid&0xff000000) != 0x01000000) return 0; + if((itm_pid&0xff000000) != 0) return 0; + if((itm_pid&0xffffff) != 144) return 0; + static const DWORD _stat_level=0x4AEF48; + DWORD curr_hp, max_hp; + __asm { + mov eax, target; + mov edx, 35; + call _stat_level; + mov curr_hp, eax; + mov eax, target; + mov edx, 7; + call _stat_level; + mov max_hp, eax; + } + if(curr_hp, int *aRoundsSpent +static const DWORD add_check_for_item_ammo_cost_back = 0x4266EE; +// adds check for weapons which require more than 1 ammo for single shot (super cattle prod & mega power fist) +static void __declspec(naked) add_check_for_item_ammo_cost() { + __asm { + push edx + push ebx + sub esp, 4 + call item_w_cur_ammo_ + mov ebx, eax + mov eax, ecx // weapon + mov edx, esp + mov dword ptr [esp], 1 + pushad + push 1 // hook type + call AmmoCostHookWrapper + add esp, 4 + popad + mov eax, [esp] + cmp eax, ebx + jle enoughammo + xor eax, eax // this will force "Out of ammo" + jmp end +enoughammo: + mov eax, 1 // this will force success +end: + add esp, 4 + pop ebx + pop edx + jmp add_check_for_item_ammo_cost_back; // jump back + } +} + +static const DWORD divide_burst_rounds_by_ammo_cost_back = 0x4234B9; +static void __declspec(naked) divide_burst_rounds_by_ammo_cost() { + __asm { + // ecx - current ammo, eax - burst rounds; need to set ebp + push edx + sub esp, 4 + mov ebp, eax + mov eax, edx // weapon + mov dword ptr [esp], 1 + mov edx, esp // *rounds + pushad + push 2 + call AmmoCostHookWrapper + add esp, 4 + popad + mov edx, 0 + mov eax, ebp // rounds in burst + imul dword ptr [esp] // so much ammo is required for this burst + cmp eax, ecx + jle skip + mov eax, ecx // if more than current ammo, set it to current +skip: + idiv dword ptr [esp] // divide back to get proper number of rounds for damage calculations + mov ebp, eax + add esp, 4 + pop edx + // end overwriten code + jmp divide_burst_rounds_by_ammo_cost_back; // jump back + } +} + +void InventoryInit() { + mode=GetPrivateProfileInt("Misc", "CritterInvSizeLimitMode", 0, ini); + invenapcost=GetPrivateProfileInt("Misc", "InventoryApCost", 4, ini); + invenapqpreduction=GetPrivateProfileInt("Misc", "QuickPocketsApCostReduction", 2, ini); + MakeCall(0x46E80B, inven_ap_cost_hook, true); + if(mode>7) mode=0; + if(mode>=4) { + mode-=4; + SafeWrite8(0x477EB3, 0xeb); + } + if(mode) { + MaxItemSize=GetPrivateProfileInt("Misc", "CritterInvSizeLimit", 100, ini); + + //Check item_add_multi (picking stuff from the floor, etc.) + HookCall(0x4771BD, &ItemAddMultiHook1); + MakeCall(0x47726D, &ItemAddMultiHook2, true); + MakeCall(0x42E688, &CritterIsOverloadedHook, true); + + //Check capacity of player and barteree when bartering + MakeCall(0x474C78, &BarterAttemptTransactionHook1, true); + MakeCall(0x474CCF, &BarterAttemptTransactionHook2, true); + + //Display total weight on the inventory screen + SafeWrite32(0x4725FF, (DWORD)&InvenFmt); + MakeCall(0x4725E0, &DisplayStatsHook, true); + SafeWrite8(0x47260F, 0x20); + SafeWrite32(0x4725F9, 0x9c+0xc); + SafeWrite8(0x472606, 0x10+0xc); + SafeWrite32(0x472632, 150); + SafeWrite8(0x472638, 0); + + //Display item weight when examening + HookCall(0x472FFE, &InvenObjExamineFuncHook); + } + + if(GetPrivateProfileInt("Misc", "SuperStimExploitFix", 0, ini)) { + GetPrivateProfileString("sfall", "SuperStimExploitMsg", "You cannot use a super stim on someone who is not injured!", SuperStimMsg, 128, translationIni); + MakeCall(0x49C3CC, SuperStimFix, true); + } + + if(GetPrivateProfileInt("Misc", "CheckWeaponAmmoCost", 0, ini)) { + MakeCall(0x4266E9, &add_check_for_item_ammo_cost, true); + MakeCall(0x4234B3, ÷_burst_rounds_by_ammo_cost, true); + } + + ReloadWeaponKey = GetPrivateProfileInt("Input", "ReloadWeaponKey", 0, ini); +} +void InventoryReset() { + invenapcost=GetPrivateProfileInt("Misc", "InventoryApCost", 4, ini); +} \ No newline at end of file diff --git a/sfall/Inventory.h b/sfall/Inventory.h new file mode 100644 index 00000000..117a8ad9 --- /dev/null +++ b/sfall/Inventory.h @@ -0,0 +1,24 @@ +/* + * sfall + * Copyright (C) 2011 Timeslip + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void _stdcall SetInvenApCost(int a); +void InventoryInit(); +void InventoryReset(); +void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey); diff --git a/sfall/KillCounter.cpp b/sfall/KillCounter.cpp new file mode 100644 index 00000000..8da63e56 --- /dev/null +++ b/sfall/KillCounter.cpp @@ -0,0 +1,115 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "version.h" + +static int usingExtraKillTypes; +bool UsingExtraKillTypes() { return usingExtraKillTypes!=0; } + +//Fallout's idea of _fastcall seems to be different to VS2005's. +//Might as well do this in asm, or the custom prolog code would end up being longer than the function +static DWORD __declspec(naked) ReadKillCounter(DWORD killtype) { + //if(killtype>38) return 0; + //return ((WORD*)0x0056D780)[killtype]; + __asm { + cmp eax, 38; + jle func; + xor eax, eax; + ret; +func: + push ebx; + lea ebx, ds:[0x0056D780+eax*2]; + xor eax,eax; + mov ax, word ptr [ebx] + pop ebx; + ret; + } +} + +static void __declspec(naked) IncKillCounter(DWORD killtype) { + //if(killtype>38) return; + //((WORD*)0x0056D780)[killtype]++; + __asm { + cmp eax, 38; + jle func; + ret; +func: + push ebx; + lea ebx, ds:[0x0056D780+eax*2]; + xor eax, eax; + mov ax, word ptr [ebx]; + inc ax; + mov word ptr [ebx], ax; + pop ebx; + ret; + } +} + +void KillCounterInit(bool use) { + if(!use) { + usingExtraKillTypes=0; + return; + } + usingExtraKillTypes=1; + + //Overwrite the function that reads the kill counter with my own + SafeWrite32(0x004344C0, ((DWORD)&ReadKillCounter) - 0x004344C4); + SafeWrite32(0x0043A163, ((DWORD)&ReadKillCounter) - 0x0043A167); + SafeWrite32(0x004571D9, ((DWORD)&ReadKillCounter) - 0x004571DD); + + //Overwrite the function that increments the kill counter with my own + SafeWrite32(0x00425145, ((DWORD)&IncKillCounter) - 0x00425149); + + //Edit the GetKillTypeName function to accept kill types over 0x13 + SafeWrite8(0x0042D980, 38); + SafeWrite8(0x0042D990, 38); + + //And the same for GetKillTypeDesc + SafeWrite8(0x0042D9C0, 38); + SafeWrite8(0x0042D9D0, 38); + SafeWrite32(0x0042D9DD, 1488); + + //Change char sheet to loop through the extra kill types + SafeWrite8(0x004344E4, 38); + + //Where fallout clears the counters + /*SafeWrite32(0x0042CF5E, sizeof(KillCounters)); + SafeWrite32(0x0042CFEC, sizeof(KillCounters)); + SafeWrite32(0x0042D863, sizeof(KillCounters)); + SafeWrite32(0x0042CF63, (DWORD)KillCounters); + SafeWrite32(0x0042CFF1, (DWORD)KillCounters); + SafeWrite32(0x0042D868, (DWORD)KillCounters); + + //Where fallout increments the kill counter + SafeWrite8(0x0042D881, COUNTERS); + SafeWrite32(0x0042D895, (DWORD)KillCounters); + SafeWrite32(0x0042D89E, (DWORD)KillCounters); + + //A function that reads the kill counter + SafeWrite8(0x0042D8AF, COUNTERS); + SafeWrite32(0x0042D8B8, (DWORD)KillCounters); + + //Not sure what these two do. Possibly related to loading the names/descriptions? + SafeWrite32(0x0042D8C6, COUNTERS); //This one causes a crash on load? + SafeWrite32(0x0042D8CB, (DWORD)KillCounters); + + SafeWrite32(0x0042D8F6, COUNTERS); + SafeWrite32(0x0042D8FB, (DWORD)KillCounters);*/ +} diff --git a/sfall/KillCounter.h b/sfall/KillCounter.h new file mode 100644 index 00000000..ec687921 --- /dev/null +++ b/sfall/KillCounter.h @@ -0,0 +1,22 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void KillCounterInit(bool use); +bool UsingExtraKillTypes(); \ No newline at end of file diff --git a/sfall/Knockback.cpp b/sfall/Knockback.cpp new file mode 100644 index 00000000..3d8931aa --- /dev/null +++ b/sfall/Knockback.cpp @@ -0,0 +1,341 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2011, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include "Knockback.h" +#include "vector9x.cpp" + +static vector NoBursts; + +struct KnockbackModifier { + DWORD id; + DWORD type; + double value; +}; + +static vector mTargets; +static vector mAttackers; +static vector mWeapons; + +struct ChanceModifier { + DWORD id; + int maximum; + int mod; +}; + +static vector HitChanceMods; +static vector PickpocketMods; + +static ChanceModifier BaseHitChance; +static ChanceModifier BasePickpocket; + +static bool hookedAimedShot; +static const DWORD item_w_damage_type=0x478570; +static const DWORD aimedShotRet1=0x478EE4; +static const DWORD aimedShotRet2=0x478EEA; +static vector disabledAS; +static vector forcedAS; + +static double ApplyModifiers(vector* mods, DWORD id, double val) { + for(DWORD i=0;isize();i++) { + if((*mods)[i].id==id) { + KnockbackModifier* mod=&(*mods)[i]; + switch(mod->type) { + case 0: val=mod->value; break; + case 1: val*=mod->value; break; + } + break; + } + } + return val; +} +static DWORD _stdcall CalcKnockback(int flags, int damage,DWORD target,DWORD attacker,DWORD weapon) { + double result=(double)damage/(flags==0x3d?5.0:10.0); + result=ApplyModifiers(&mWeapons, weapon, result); + result=ApplyModifiers(&mAttackers, attacker, result); + result=ApplyModifiers(&mTargets, target, result); + return (DWORD)floor(result); +} + +static const DWORD KnockbackRetAddr=0x00424B85; +static void __declspec(naked) KnockbackHook() { + __asm { + mov ecx, [esp+0x14]; + push ebx; + mov ebx, [esi+8]; + push ebx; //Weapon + mov ebx, [esi]; + push ebx; //Attacker + push ecx; //target + mov edi, [edi]; + push edi; //Damage + push eax; //Weapon flags + call CalcKnockback; + pop ebx; + mov ecx, [esp+0x10]; + mov [ecx], eax; + jmp KnockbackRetAddr; + } +} + +static const DWORD KnockbackRetAddr2=0x4136E3; +static void __declspec(naked) KnockbackHook2() { + __asm { + push ecx + push -1; //Weapon + push -1; //Attacker + push esi; //target + push ebx; //Damage + push 0; //Weapon flags + call CalcKnockback; + pop ecx + mov [ecx], eax; + jmp KnockbackRetAddr2; + } +} + +static const DWORD KnockbackAddr=(DWORD)&KnockbackHook; + +static int _stdcall PickpocketHook2(int base, DWORD critter) { + for(DWORD i=0;i* mods; + switch(on) { + case 0: mods=&mWeapons; break; + case 1: mods=&mTargets; break; + case 2: mods=&mAttackers; break; + default: return; + } + KnockbackModifier mod = { id, type, (double)val }; + for(DWORD i=0;isize();i++) { + if((*mods)[i].id==id) { + (*mods)[i] = mod; + return; + } + } + mods->push_back(mod); +} + +void _stdcall KnockbackRemoveMod(DWORD id, DWORD on) { + vector* mods; + switch(on) { + case 0: mods=&mWeapons; break; + case 1: mods=&mTargets; break; + case 2: mods=&mAttackers; break; + default: return; + } + for(DWORD i=0;isize();i++) { + if((*mods)[i].id==id) { + mods->remove_at(i); + return; + } + } +} + +void _stdcall SetHitChanceMax(DWORD critter, DWORD maximum, DWORD mod) { + if(critter==-1) { + BaseHitChance.maximum=maximum; + BaseHitChance.mod=mod; + return; + } + for(DWORD i=0;i. + */ + +#pragma once + +void _stdcall SetPickpocketMax(DWORD critter, DWORD maximum, DWORD mod); +void _stdcall SetHitChanceMax(DWORD critter, DWORD maximum, DWORD mod); +void _stdcall KnockbackSetMod(DWORD id, DWORD type, float val, DWORD on); +void _stdcall KnockbackRemoveMod(DWORD id, DWORD on); +void KnockbackInit(); +void Knockback_OnGameLoad(); +void _stdcall SetNoBurstMode(DWORD critter, DWORD on); +void _stdcall DisableAimedShots(DWORD pid); +void _stdcall ForceAimedShots(DWORD pid); \ No newline at end of file diff --git a/sfall/LoadGameHook.cpp b/sfall/LoadGameHook.cpp new file mode 100644 index 00000000..cc5c3560 --- /dev/null +++ b/sfall/LoadGameHook.cpp @@ -0,0 +1,567 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2011, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "LoadGameHook.h" +#include "FalloutEngine.h" +#include "graphics.h" +#include "ScriptExtender.h" +#include "version.h" +#include "Knockback.h" +#include "input.h" +#include "perks.h" +#include "Criticals.h" +#include "Console.h" +#include "FileSystem.h" +#include "HeroAppearance.h" +#include "Logging.h" +#include "movies.h" +#include "SuperSave.h" +#include "skills.h" +#include "AI.h" +#include "Inventory.h" +#include "Arrays.h" +#include "sound.h" +#include "PartyControl.h" + +#define MAX_GLOBAL_SIZE (MaxGlobalVars*12 + 4) + +static DWORD InLoop=0; +DWORD GainStatFix=0; +static DWORD SaveInCombatFix; + +DWORD InWorldMap() { return (InLoop&WORLDMAP)?1:0; } +DWORD InCombat() { return (InLoop&COMBAT)?1:0; } +DWORD GetCurrentLoops() { return InLoop; } + +static void ModifyGainXXXPerks() { + SafeWrite8(0x004AF11F, 0xeb); //Strength + SafeWrite8(0x004AF181, 0xeb); //Perception + SafeWrite8(0x004AF19D, 0xeb); //Endurance + SafeWrite8(0x004AF1BD, 0xeb); //Charisma + SafeWrite8(0x004AF214, 0xeb); //Intelligance + SafeWrite8(0x004AF230, 0xeb); //Agility + SafeWrite8(0x004AF24B, 0xeb); //Luck +} +static void RestoreGainXXXPerks() { + SafeWrite8(0x004AF11F, 0x74); //Strength + SafeWrite8(0x004AF181, 0x74); //Perception + SafeWrite8(0x004AF19D, 0x74); //Endurance + SafeWrite8(0x004AF1BD, 0x74); //Charisma + SafeWrite8(0x004AF214, 0x74); //Intelligance + SafeWrite8(0x004AF230, 0x74); //Agility + SafeWrite8(0x004AF24B, 0x74); //Luck +} + +static void _stdcall ResetState(DWORD onLoad) { + if(!onLoad) FileSystemReset(); + ClearGlobalScripts(); + ClearGlobals(); + ForceGraphicsRefresh(0); + WipeSounds(); + if(GraphicsMode>3) graphics_OnGameLoad(); + Knockback_OnGameLoad(); + Skills_OnGameLoad(); + InLoop=0; + GainStatFix=0; + RestoreGainXXXPerks(); + PerksReset(); + InventoryReset(); + RegAnimCombatCheck(1); + AfterAttackCleanup(); + PartyControlReset(); +} + +void GetSavePath(char* buf, int type) { + int saveid = *(int*)0x005193B8 + 1 +LSPageOffset;//add SuperSave Page offset + char buf2[6]; + //Fallout saving is independent of working directory + struct sPath { + char* path; + int a; + int b; + sPath* next; + }; + sPath* spath=*(sPath**)0x006B24D0; + while(spath->a&&spath->next) spath=spath->next; + + //strcpy_s(buf, MAX_PATH, **(char***)0x006B24D0); + strcpy_s(buf, MAX_PATH, spath->path); + strcat_s(buf, MAX_PATH, "\\savegame\\slot"); + _itoa_s(saveid, buf2, 10); + if(strlen(buf2)==1) strcat_s(buf, MAX_PATH, "0"); + strcat_s(buf, MAX_PATH, buf2); + switch(type) { + case 0: + strcat_s(buf, MAX_PATH, "\\sfallgv.sav"); + break; + case 1: + strcat_s(buf, MAX_PATH, "\\sfallfs.sav"); + break; + } +} + +static char SaveSfallDataFailMsg[128]; + +static void _stdcall SaveGame2() { + char buf[MAX_PATH]; + GetSavePath(buf, 0); + +#ifdef TRACE + dlog_f("Saving game: %s\r\n", DL_MAIN, buf); +#endif + + DWORD unused; + DWORD unused2=0; + HANDLE h=CreateFileA(buf, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if(h!=INVALID_HANDLE_VALUE) { + SaveGlobals(h); + WriteFile(h, &unused2, 4, &unused, 0); + WriteFile(h, &GainStatFix, 4, &unused, 0); + PerksSave(h); + SaveArrays(h); + CloseHandle(h); + } else { + dlogr("ERROR creating sfallgv!", DL_MAIN); + DisplayConsoleMessage(SaveSfallDataFailMsg); + PlaySfx("IISXXXX1"); + } + GetSavePath(buf, 1); + h=CreateFileA(buf, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if(h!=INVALID_HANDLE_VALUE) { + FileSystemSave(h); + } + CloseHandle(h); +} + +static char SaveFailMsg[128]; +static DWORD _stdcall combatSaveTest() { + if(!SaveInCombatFix) return 1; + if(InLoop&COMBAT) { + if(SaveInCombatFix==2 || !(InLoop&PCOMBAT)) { + DisplayConsoleMessage(SaveFailMsg); + return 0; + } + DWORD ap; + DWORD bonusmove; + __asm { + mov edx, 8; + mov eax, ds:[0x6610B8]; + mov ebx, 0x4AEF48; + call ebx; + mov ap, eax; + mov eax, ds:[0x6610B8]; + mov edx, 3; + mov ebx, 0x496B78; + call ebx; + mov bonusmove, eax; + } + if(*(DWORD*)(*(DWORD*)0x6610B8 + 0x40) != ap || bonusmove*2!=*(DWORD*)0x0056D39C) { + DisplayConsoleMessage(SaveFailMsg); + return 0; + } + } + return 1; +} +static void __declspec(naked) SaveGame() { + __asm { + push ebx; + push ecx; + push edx; + + mov edx, eax; + call combatSaveTest; + test eax, eax; + jz end; + mov eax, edx; + + or InLoop, SAVEGAME; + mov ebx, 0x0047B88C; + call ebx; + and InLoop, (-1^SAVEGAME); + cmp eax, 1; + jne end; + call SaveGame2; + mov eax, 1; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +// should be called before savegame is loaded +static void _stdcall LoadGame2_Before() { + ResetState(1); +} + +static void _stdcall LoadGame2_After() { + char buf[MAX_PATH]; + GetSavePath(buf, 0); + +#ifdef TRACE + dlog_f("Loading save game: %s\r\n", DL_MAIN, buf); +#endif + + ClearGlobals(); + HANDLE h=CreateFileA(buf, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if(h!=INVALID_HANDLE_VALUE) { + DWORD size=0; + DWORD unused; + LoadGlobals(h); + ReadFile(h, (&unused), 4, &size, 0); + ReadFile(h, &GainStatFix, 4, &size, 0); + if(!size) GainStatFix = 0; + else { + PerksLoad(h); + LoadArrays(h); + } + CloseHandle(h); + } else { + GainStatFix = 0; + dlogr("Cannot read sfallgv.sav - assuming non-sfall save.", DL_MAIN); + } + + if(GainStatFix) ModifyGainXXXPerks(); + else RestoreGainXXXPerks(); + + LoadGlobalScripts(); + CritLoad(); + LoadHeroAppearance(); +} + +static void __declspec(naked) LoadSlot() { + __asm { + pushad; + call LoadGame2_Before; + popad; + push ebx; + mov ebx, 0x0047DC68; + call ebx; + pop ebx; + retn; + } +} + +static void __declspec(naked) LoadGame() { + __asm { + push ebx; + push ecx; + push edx; + or InLoop, LOADGAME; + mov ebx, 0x0047C640; + call ebx; + /*push eax; + push 0x0000101f; + push 0x0045E949; + call SafeWrite32;*/ + and InLoop, (-1^LOADGAME); + //pop eax; + cmp eax, 1; + jne end; + call LoadGame2_After; + mov eax, 1; +end: + + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void NewGame2() { + ResetState(0); + + dlogr("Starting new game", DL_MAIN); + + SetNewCharAppearanceGlobals(); + + if(GetPrivateProfileInt("Misc", "GainStatPerkFix", 1, ini)) { + ModifyGainXXXPerks(); + GainStatFix=1; + } else { + RestoreGainXXXPerks(); + GainStatFix=0; + } + + if(GetPrivateProfileInt("Misc", "PipBoyAvailableAtGameStart", 0, ini)) { + SafeWrite8(0x00596C7B, 1); + } + if(GetPrivateProfileInt("Misc", "DisableHorrigan", 0, ini)) { + *(DWORD*)0x00672E04=1; + } + + LoadGlobalScripts(); + CritLoad(); +} +static void __declspec(naked) NewGame() { + __asm { + pushad; + call NewGame2; + popad; + push ebx; + mov ebx, 0x00480E48; + call ebx; + pop ebx; + retn; + } +} + +static void __declspec(naked) MainMenu() { + __asm { + pushad; + push 0; + call ResetState; + call LoadHeroAppearance; + popad; + push ebx; + mov ebx, 0x00481AEC; + call ebx; + pop ebx; + retn; + } +} +static void __declspec(naked) WorldMapHook() { + __asm { + push ebx; + or InLoop, WORLDMAP; + xor eax, eax; + mov ebx, 0x004BFE10; + call ebx; + and InLoop, (-1^WORLDMAP); + pop ebx; + retn; + } +} +static void __declspec(naked) WorldMapHook2() { + __asm { + push ebx; + or InLoop, WORLDMAP; + mov ebx, 0x004BFE10; + call ebx; + and InLoop, (-1^WORLDMAP); + pop ebx; + retn; + } +} +static void __declspec(naked) CombatHook() { + __asm { + push ebx; + pushad; + call AICombatStart; + popad + or InLoop, COMBAT; + mov ebx, 0x00422D2C; + call ebx; + pushad; + call AICombatEnd; + popad + and InLoop, (-1^COMBAT); + pop ebx; + retn; + } +} +static void __declspec(naked) PlayerCombatHook() { + __asm { + push ebx; + or InLoop, PCOMBAT; + mov ebx, 0x004227F4; + call ebx; + and InLoop, (-1^PCOMBAT); + pop ebx; + retn; + } +} +static void __declspec(naked) EscMenuHook() { + __asm { + push ebx; + or InLoop, ESCMENU; + mov ebx, 0x0048FC50; + call ebx; + and InLoop, (-1^ESCMENU); + pop ebx; + retn; + } +} +static void __declspec(naked) EscMenuHook2() { + //Bloody stupid watcom compiler optimizations... + __asm { + push ebx; + or InLoop, ESCMENU; + mov ebx, 0x0048FC48; + call ebx; + and InLoop, (-1^ESCMENU); + pop ebx; + retn; + } +} +static void __declspec(naked) OptionsMenuHook() { + __asm { + push ebx; + or InLoop, OPTIONS; + mov ebx, 0x00490798; + call ebx; + and InLoop, (-1^OPTIONS); + pop ebx; + retn; + } +} +static void __declspec(naked) HelpMenuHook() { + __asm { + push ebx; + or InLoop, HELP; + mov ebx, 0x00443F74; + call ebx; + and InLoop, (-1^HELP); + pop ebx; + retn; + } +} +static void __declspec(naked) CharacterHook() { + __asm { + push ebx; + or InLoop, CHARSCREEN; + pushad; + call PerksEnterCharScreen; + popad; + mov ebx, 0x00431DF8; + call ebx; + pushad; + test eax, eax; + jz success; + call PerksCancelCharScreen; + jmp end; +success: + call PerksAcceptCharScreen; +end: + popad; + and InLoop, (-1^CHARSCREEN); + pop ebx; + retn; + } +} +static void __declspec(naked) DialogHook() { + __asm { + push ebx; + or InLoop, DIALOG; + mov ebx, 0x004465C0; + call ebx; + and InLoop, (-1^DIALOG); + pop ebx; + retn; + } +} +static void __declspec(naked) PipboyHook() { + __asm { + push ebx; + or InLoop, PIPBOY; + mov ebx, 0x00497004; + call ebx; + and InLoop, (-1^PIPBOY); + pop ebx; + retn; + } +} +static void __declspec(naked) SkilldexHook() { + __asm { + push ebx; + or InLoop, SKILLDEX; + mov ebx, 0x004ABFD0; + call ebx; + and InLoop, (-1^SKILLDEX); + pop ebx; + retn; + } +} +static void __declspec(naked) InventoryHook() { + __asm { + push ebx; + or InLoop, INVENTORY; + mov ebx, 0x0046E7B0; + call ebx; + and InLoop, (-1^INVENTORY); + pop ebx; + retn; + } +} +static void __declspec(naked) AutomapHook() { + __asm { + push ebx; + or InLoop, AUTOMAP; + mov ebx, 0x0041B8BC; + call ebx; + and InLoop, (-1^AUTOMAP); + pop ebx; + retn; + } +} + +void LoadGameHookInit() { + SaveInCombatFix=GetPrivateProfileInt("Misc", "SaveInCombatFix", 1, ini); + if(SaveInCombatFix>2) SaveInCombatFix=0; + if(SaveInCombatFix) { + GetPrivateProfileString("sfall", "SaveInCombat", "Cannot save at this time", SaveFailMsg, 128, translationIni); + } + GetPrivateProfileString("sfall", "SaveSfallDataFail", "ERROR saving extended savegame information! Check if other programs interfere with savegame files/folders and try again!", SaveSfallDataFailMsg, 128, translationIni); + + HookCall(0x480AB3, NewGame); + + HookCall(0x47C72C, LoadSlot); + HookCall(0x47D1C9, LoadSlot); + HookCall(0x443AE4, LoadGame); + HookCall(0x443B89, LoadGame); + HookCall(0x480B77, LoadGame); + HookCall(0x48FD35, LoadGame); + HookCall(0x443AAC, SaveGame); + HookCall(0x443B1C, SaveGame); + HookCall(0x48FcFF, SaveGame); + + HookCall(0x480A28, MainMenu); + + HookCall(0x483668, WorldMapHook); + HookCall(0x4A4073, WorldMapHook); + HookCall(0x4C4855, WorldMapHook2); + HookCall(0x426A29, CombatHook); + HookCall(0x4432BE, CombatHook); + HookCall(0x45F6D2, CombatHook); + HookCall(0x4A4020, CombatHook); + HookCall(0x4A403D, CombatHook); + HookCall(0x422B09, PlayerCombatHook); + HookCall(0x480C16, EscMenuHook); + HookCall(0x4433BE, EscMenuHook2); + HookCall(0x48FCE4, OptionsMenuHook); + HookCall(0x48FD17, OptionsMenuHook); + HookCall(0x48FD4D, OptionsMenuHook); + HookCall(0x48FD6A, OptionsMenuHook); + HookCall(0x48FD87, OptionsMenuHook); + HookCall(0x48FDB3, OptionsMenuHook); + HookCall(0x443A50, HelpMenuHook); + HookCall(0x443320, CharacterHook); + HookCall(0x4A73EB, CharacterHook); + HookCall(0x4A740A, CharacterHook); + HookCall(0x445748, DialogHook); + HookCall(0x443463, PipboyHook); + HookCall(0x443605, PipboyHook); + HookCall(0x4434AC, SkilldexHook); + HookCall(0x44C7BD, SkilldexHook); + HookCall(0x443357, InventoryHook); + HookCall(0x44396D, AutomapHook); + HookCall(0x479519, AutomapHook); +} diff --git a/sfall/LoadGameHook.h b/sfall/LoadGameHook.h new file mode 100644 index 00000000..14779df2 --- /dev/null +++ b/sfall/LoadGameHook.h @@ -0,0 +1,43 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void LoadGameHookInit(); + +DWORD InWorldMap(); +DWORD InCombat(); + +#define WORLDMAP (1<<0) //0x1 +#define LOCALMAP (1<<1) //0x2 No point hooking this: would always be 1 at any point at which scripts are running +#define DIALOG (1<<2) //0x4 +#define ESCMENU (1<<3) //0x8 +#define SAVEGAME (1<<4) //0x10 +#define LOADGAME (1<<5) //0x20 +#define COMBAT (1<<6) //0x40 +#define OPTIONS (1<<7) //0x80 +#define HELP (1<<8) //0x100 +#define CHARSCREEN (1<<9) //0x200 +#define PIPBOY (1<<10)//0x400 +#define PCOMBAT (1<<11)//0x800 +#define INVENTORY (1<<12)//0x1000 +#define AUTOMAP (1<<13)//0x2000 +#define SKILLDEX (1<<14)//0x4000 +#define RESERVED (1<<31) + +DWORD GetCurrentLoops(); \ No newline at end of file diff --git a/sfall/Logging.cpp b/sfall/Logging.cpp new file mode 100644 index 00000000..21f0c4d5 --- /dev/null +++ b/sfall/Logging.cpp @@ -0,0 +1,68 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" +#include "logging.h" + +#ifdef TRACE + +#include "main.h" +#include + +using namespace std; + +static int DebugTypes=0; +static ofstream Log; + +void dlog(const char* a, int type) { + if(type!=DL_MAIN&&!(type&DebugTypes)) return; + Log << a; + Log.flush(); +} +void dlogr(const char* a, int type) { + if(type!=DL_MAIN&&!(type&DebugTypes)) return; + Log << a << "\r\n"; + Log.flush(); +} + +void LoggingInit() { + Log.open("sfall-log.txt", ios_base::out | ios_base::trunc); + if(GetPrivateProfileIntA("Debugging", "Init", 0, ".\\ddraw.ini")) DebugTypes|=DL_INIT; + if(GetPrivateProfileIntA("Debugging", "Hook", 0, ".\\ddraw.ini")) DebugTypes|=DL_HOOK; + if(GetPrivateProfileIntA("Debugging", "Script", 0, ".\\ddraw.ini")) DebugTypes|=DL_SCRIPT; + if(GetPrivateProfileIntA("Debugging", "Criticals", 0, ".\\ddraw.ini")) DebugTypes|=DL_CRITICALS; +} + +#endif + +void dlog_f(const char *format, int type, ...) { + +#ifdef TRACE + + int i; + va_list arg; + char buf[1024]; + va_start(arg, format); + i = vsprintf(buf, format, arg); + va_end(arg); + Log << buf; + Log.flush(); + +#endif + +} \ No newline at end of file diff --git a/sfall/Logging.h b/sfall/Logging.h new file mode 100644 index 00000000..4d556dcf --- /dev/null +++ b/sfall/Logging.h @@ -0,0 +1,38 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//#define TRACE + +#define DL_MAIN (0) +#define DL_INIT (1<<1) +#define DL_HOOK (1<<2) +#define DL_SCRIPT (1<<3) +#define DL_CRITICALS (1<<4) + +#ifdef TRACE +#include + +void dlog(const char* msg, int type); +void dlogr(const char* msg, int type); +void LoggingInit(); +#else +#define dlog(a,b) +#define dlogr(a,b) +#endif + +void dlog_f(const char *format, int type, ...); \ No newline at end of file diff --git a/sfall/MainMenu.cpp b/sfall/MainMenu.cpp new file mode 100644 index 00000000..9dac5519 --- /dev/null +++ b/sfall/MainMenu.cpp @@ -0,0 +1,109 @@ +/* + * sfall + * Copyright (C) 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" +#include "version.h" + +static DWORD MainMenuYOffset; +static DWORD MainMenuTextOffset; + +static const DWORD MainMenuButtonYHookRet=0x48184A; +static void __declspec(naked) MainMenuButtonYHook() { + __asm { + xor edi, edi; + xor esi, esi; + mov ebp, MainMenuYOffset; + jmp MainMenuButtonYHookRet; + } +} + +static void __declspec(naked) MainMenuTextYHook() { + __asm { + add eax, MainMenuTextOffset; + jmp dword ptr ds:[0x51E3B8]; + } +} + +static const DWORD MainMenuTextRet=0x4817B0; +static const DWORD _win_print=0x4D684C; +static const char* VerString1 = "SFALL " VERSION_STRING; +static DWORD OverrideColour; +static void __declspec(naked) FontColour() { + __asm { + cmp OverrideColour, 0; + je skip; + mov eax, OverrideColour; + retn; +skip: + movzx eax, byte ptr ds:[0x6A8B33]; + or eax, 0x6000000; + retn; + } +} +static void __declspec(naked) MainMenuTextHook() { + __asm { + mov edi, [esp]; + sub edi, 12; //shift yposition up by 12 + mov [esp], edi; + mov ebp, ecx; + push eax; + call FontColour; + mov [esp+8], eax; + pop eax; + call _win_print; + call FontColour; + push eax;//colour + mov edx, VerString1;//msg + xor ebx, ebx;//font + mov ecx, ebp; + dec ecx; //xpos + add edi, 12; + push edi; //ypos + mov eax, dword ptr ds:[0x5194F0];//winptr + call _win_print; + jmp MainMenuTextRet; + } +} + +void MainMenuInit() { + int tmp; + + if(tmp=GetPrivateProfileIntA("Misc", "MainMenuCreditsOffsetX", 0, ini)) { + SafeWrite32(0x481753, 0xf+tmp); + } + if(tmp=GetPrivateProfileIntA("Misc", "MainMenuCreditsOffsetY", 0, ini)) { + SafeWrite32(0x48175C, 0x1cc+tmp); + } + if(tmp=GetPrivateProfileIntA("Misc", "MainMenuOffsetX", 0, ini)) { + SafeWrite32(0x48187C, 0x1e+tmp); + MainMenuTextOffset=tmp; + } + if(tmp=GetPrivateProfileIntA("Misc", "MainMenuOffsetY", 0, ini)) { + MainMenuYOffset=tmp; + MainMenuTextOffset+=tmp*640; + MakeCall(0x481844, &MainMenuButtonYHook, true); + } + if(MainMenuTextOffset) { + SafeWrite8(0x481933, 0x90); + MakeCall(0x481934, &MainMenuTextYHook, false); + } + + MakeCall(0x4817AB, MainMenuTextHook, true); + OverrideColour=GetPrivateProfileInt("Misc", "MainMenuFontColour", 0, ini); + if(OverrideColour) MakeCall(0x48174C, &FontColour, false); +} \ No newline at end of file diff --git a/sfall/MainMenu.h b/sfall/MainMenu.h new file mode 100644 index 00000000..87739e83 --- /dev/null +++ b/sfall/MainMenu.h @@ -0,0 +1,19 @@ +/* + * sfall + * Copyright (C) 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +void MainMenuInit(); \ No newline at end of file diff --git a/sfall/Numbers.cpp b/sfall/Numbers.cpp new file mode 100644 index 00000000..92dc7ea1 --- /dev/null +++ b/sfall/Numbers.cpp @@ -0,0 +1,53 @@ +#include "Numbers.h" +#include "Imports.h" +#include "Defines.h" +#include "SafeWrite.h" +#include "Input.h" + +static void RunNumbers(DWORD load) { + if(!load) { + + } + exit(0); +} + +static void NewGameRet=0x481A00; +static void _declspec(naked) NewGameHook() { + _asm { + pushad; + push DIK_LSHIFT; + call KeyDown; + test eax, eax; + jz fail; + popad; + xor eax, eax; + push eax; + call RunNumbers(); +fail: + popad; + jmp NewGameRet; + } +} + +static void LoadGameRet=0x480AFE; +static void _declspec(naked) LoadGameHook() { + _asm { + pushad; + push DIK_LSHIFT; + call KeyDown; + test eax, eax; + jz fail; + popad; + push 1; + call RunNumbers(); +fail: + popad; + mov ecx, 0x1e0; + jmp LoadGameRet; + } +} + +NumbersInit() { + HookCall(0x480A81, &NewGameHook); + MakeCall(0x480AF9, &LoadGameHook, true); +} \ No newline at end of file diff --git a/sfall/Numbers.h b/sfall/Numbers.h new file mode 100644 index 00000000..57a67d65 --- /dev/null +++ b/sfall/Numbers.h @@ -0,0 +1,4 @@ +#define WIN32_LEAN_AND_MEAN +#include + +void NumbersInit(); \ No newline at end of file diff --git a/sfall/PartyControl.cpp b/sfall/PartyControl.cpp new file mode 100644 index 00000000..911af759 --- /dev/null +++ b/sfall/PartyControl.cpp @@ -0,0 +1,249 @@ +/* + * sfall + * Copyright (C) 2013 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + KNOWN ISSUES with party control + - doesn't work with NPC's wearing armor mod, armor won't change when you change it from critter's inventory + - shows perks and some other info for the main dude in character screen +*/ + +#include "main.h" +#include "FalloutEngine.h" +#include "PartyControl.h" +#include + +static DWORD Mode; +static int IsControllingNPC = 0; +static std::vector Chars; + +TGameObj* real_dude = NULL; +DWORD real_traits[2]; + +static const DWORD _combat_turn=0x42299C; +static const DWORD _tile_refresh_display=0x4B12D8; + +static const DWORD* list_com = (DWORD*)0x56D394; + +static bool _stdcall IsInPidList(TGameObj* obj) { + int pid = obj->pid & 0xFFFFFF; + for (std::vector::iterator it = Chars.begin(); it != Chars.end(); it++) { + if (*it == pid) { + return true; + } + } + return false; +} + +static void _stdcall SetInventoryCheck(bool skip) { + if (skip) { + SafeWrite16(0x46E7CD, 0x9090); //Inventory check + SafeWrite32(0x46E7Cf, 0x90909090); + } else { + SafeWrite16(0x46E7CD, 0x850F); //Inventory check + SafeWrite32(0x46E7Cf, 0x4B1); + } +} + +// return values: 0 - use vanilla handler, 1 - skip vanilla handler, return 0 (normal status), -1 - skip vanilla, return -1 (game ended) +static int _stdcall CombatWrapperInner(TGameObj* obj) { + if ((obj != *obj_dude_ptr) && (Chars.size() == 0 || IsInPidList(obj)) && (Mode == 1 || IsPartyMember(obj))) { + IsControllingNPC = 1; + SetInventoryCheck(true); + char dudeWeaponSlot = (char)*activeUIHand_ptr; + // save "real" dude state + real_dude = *obj_dude_ptr; + *obj_dude_ptr = obj; + *inven_dude_ptr = obj; + memcpy(real_traits, dude_traits, sizeof(DWORD)*2); + + // deduce active hand by weapon anim code + char critterAnim = (obj->artFID & 0xF000) >> 12; // current weapon as seen in hands + if (AnimCodeByWeapon(GetInvenWeaponLeft(obj)) == critterAnim) { // definitely left hand.. + *activeUIHand_ptr = 0; + } else { + *activeUIHand_ptr = 1; + } + int turnResult; + __asm { + call intface_redraw_; + mov eax, obj; + call _combat_turn; + mov turnResult, eax; + } + // restore state + if (IsControllingNPC) { // if game was loaded during turn, PartyControlReset() was called and already restored state + *activeUIHand_ptr = dudeWeaponSlot; + memcpy(dude_traits, real_traits, sizeof(DWORD)*2); + *obj_dude_ptr = real_dude; + *inven_dude_ptr = real_dude; + __asm { + call intface_redraw_; + } + SetInventoryCheck(false); + IsControllingNPC = 0; + } + // -1 means that combat ended during turn + return (turnResult == -1) ? -1 : 1; + } + return 0; +} + + +// this hook fixes NPCs art switched to main dude art after inventory screen closes +static const DWORD _obj_change_fid=0x48AA3C; +static void _declspec(naked) FidChangeHook() { + _asm { + cmp IsControllingNPC, 0; + je skip; + push eax; + mov eax, [eax+0x20]; // current fid + and eax, 0xffff0fff; + and edx, 0x0000f000; + or edx, eax; // only change one octet with weapon type + pop eax; +skip: + call _obj_change_fid; + retn; + } +} + +/* +static const DWORD item_add_force_ = 0x4772B8; +static void _declspec(naked) ItemDropHook() { + _asm { + call item_add_force_; + retn; + } +} +*/ + +// 1 skip handler, -1 don't skip +int __stdcall PartyControl_SwitchHandHook(TGameObj* item) { + if (ItemGetType(item) == 3 && IsControllingNPC > 0) { + int canUse; + /* check below uses AI packets and skills to check if weapon is usable + __asm { + mov edx, item; + mov eax, obj_dude_ptr; + mov eax, [eax]; + mov ebx, 2; + call ai_can_use_weapon_; + mov canUse, eax; + }*/ + int fId = (*obj_dude_ptr)->artFID; + char weaponCode = AnimCodeByWeapon(item); + fId = (fId & 0xffff0fff) | (weaponCode << 12); + // check if art with this weapon exists + __asm { + mov eax, fId; + call art_exists_; + mov canUse, eax; + } + if (!canUse) { + DisplayConsoleMessage(GetMessageStr(MSG_FILE_PROTO, 675)); // I can't do that + return 1; + } + } + return -1; +} + + +static void _declspec(naked) CombatWrapper_v2() { + __asm { + sub esp, 4; + pushad; + push eax; + call CombatWrapperInner; + mov [esp+32], eax; + popad; + add esp, 4; + cmp [esp-4], 0; + je gonormal; + cmp [esp-4], -1; + je combatend; + xor eax, eax; + retn; +combatend: + mov eax, -1; // don't continue combat, as the game was loaded + retn; +gonormal: + jmp _combat_turn; + } +} + +// hack to exit from this function safely when you load game during NPC turn +static const DWORD CombatHack_add_noncoms_back = 0x422359; +static void _declspec(naked) CombatHack_add_noncoms_() { + __asm { + call CombatWrapper_v2; + cmp eax, -1; + jne gonormal; + mov eax, list_com; + mov [eax], 0; + mov ecx, [esp]; +gonormal: + jmp CombatHack_add_noncoms_back; + } +} + +void PartyControlInit() { + Mode = GetPrivateProfileIntA("Misc", "ControlCombat", 0, ini); + if (Mode > 2) + Mode = 0; + if (Mode > 0) { + char pidbuf[512]; + pidbuf[511]=0; + if (GetPrivateProfileStringA("Misc", "ControlCombatPIDList", "", pidbuf, 511, ini)) { + char* ptr = pidbuf; + char* comma; + while (true) { + comma = strchr(ptr, ','); + if (!comma) + break; + *comma = 0; + if (strlen(ptr) > 0) + Chars.push_back((WORD)strtoul(ptr, 0, 0)); + ptr = comma + 1; + } + if (strlen(ptr) > 0) + Chars.push_back((WORD)strtoul(ptr, 0, 0)); + } + dlog_f(" Mode %d, Chars read: %d.", DL_INIT, Mode, Chars.size()); + + HookCall(0x46EBEE, &FidChangeHook); + //TODO: Change trait check. + + MakeCall(0x422354, &CombatHack_add_noncoms_, true); + HookCall(0x422D87, &CombatWrapper_v2); + HookCall(0x422E20, &CombatWrapper_v2); + } else + dlog(" Disabled.", DL_INIT); +} + +void __stdcall PartyControlReset() +{ + if (real_dude != NULL && IsControllingNPC > 0) { + memcpy(dude_traits, real_traits, sizeof(DWORD)*2); + *obj_dude_ptr = real_dude; + *inven_dude_ptr = real_dude; + IsControllingNPC = 0; + } + real_dude = NULL; + SetInventoryCheck(false); +} diff --git a/sfall/PartyControl.h b/sfall/PartyControl.h new file mode 100644 index 00000000..79c8296a --- /dev/null +++ b/sfall/PartyControl.h @@ -0,0 +1,22 @@ +/* +* sfall +* Copyright (C) 2013 The sfall team +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#pragma once + +void PartyControlInit(); +void __stdcall PartyControlReset(); +int __stdcall PartyControl_SwitchHandHook(TGameObj* item); \ No newline at end of file diff --git a/sfall/Perks.cpp b/sfall/Perks.cpp new file mode 100644 index 00000000..3febcba1 --- /dev/null +++ b/sfall/Perks.cpp @@ -0,0 +1,1100 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Vector9x.cpp" +#include "define.h" +#include "Perks.h" + +//static const BYTE PerksUsed=121; + +static char Name[64*119]; +static char Desc[1024*119]; +static char tName[64*16]; +static char tDesc[1024*16]; +static char perksFile[260]; +static BYTE disableTraits[16]; +static DWORD* pc_trait=(DWORD*)0x66BE40; + +#define check_trait(a) !disableTraits[a]&&(pc_trait[0]==a||pc_trait[1]==a) + +static DWORD addPerkMode=2; + +struct TraitStruct { + char* Name; + char* Desc; + DWORD Image; +}; +struct PerkStruct { + char* Name; + char* Desc; + int Image; + int Ranks; + int Level; + int Stat; + int StatMag; + int Skill1; + int Skill1Mag; + int Type; + int Skill2; + int Skill2Mag; + int Str; + int Per; + int End; + int Chr; + int Int; + int Agl; + int Lck; +}; +//static const PerkStruct BlankPerk={ &Name[119*64], &Desc[119*1024], 0x48, 1, 1, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0 }; +static PerkStruct Perks[119]; +static TraitStruct Traits[16]; + +struct FakePerk { + int Level; + int Image; + char Name[64]; + char Desc[1024]; +}; + +vector fakeTraits; +vector fakePerks; +vector fakeSelectablePerks; + +extern DWORD GainStatFix; +static DWORD RemoveTraitID; +static DWORD RemovePerkID; +static DWORD RemoveSelectableID; + +static DWORD TraitSkillBonuses[16*18]; +static DWORD TraitStatBonuses[16*(STAT_max_derived+1)]; + +static DWORD IgnoringDefaultPerks=0; +static char PerkBoxTitle[64]; + +static const DWORD trait_level=0x4B3BC8; +static DWORD PerkFreqOverride; + +void _stdcall SetPerkFreq(int i) { + PerkFreqOverride=i; +} + +static DWORD _stdcall IsTraitDisabled(int id) { + return disableTraits[id]; +} +static void __declspec(naked) LevelUpHook() { + __asm { + push ecx; + push ebx; + mov ecx, PerkFreqOverride; + test ecx, ecx; + jnz afterskilled; + mov eax, 0xe; + call trait_level; //Check if the player has the skilled trait + test eax, eax; + jz notskilled; + push 14; + call IsTraitDisabled; + test eax, eax; + jnz notskilled; + mov ecx, 4; + jmp afterskilled; +notskilled: + mov ecx, 3; +afterskilled: + mov eax, ds:[0x6681B0]; //Get players level + inc eax; + xor edx, edx; + div ecx; + test edx, edx; + jnz end; + inc byte ptr ds:[0x570A29]; //Increment the number of perks owed +end: + pop ebx; + pop ecx; + mov edx, ds:[0x6681B0]; + retn; + } +} + +static void __declspec(naked) GetPerkBoxTitleHook() { + __asm { + lea eax, PerkBoxTitle; + ret; + } +} +void _stdcall IgnoreDefaultPerks() { IgnoringDefaultPerks=1; } +void _stdcall RestoreDefaultPerks() { IgnoringDefaultPerks=0; } +void _stdcall SetPerkboxTitle(char* name) { + if(name[0]=='\0') { + SafeWrite32(0x0043C77D, 0x488cb); + } else { + strcpy_s(PerkBoxTitle, name); + HookCall(0x0043C77C, GetPerkBoxTitleHook); + } +} +void _stdcall SetSelectablePerk(char* name, int level, int image, char* desc) { + if(level<0||level>1) return; + if(level==0) { + for(DWORD i=0;i100) return; + if(level==0) { + for(DWORD i=0;i1) return; + if(level==0) { + for(DWORD i=0;iSTAT_max_derived) return 0; + int result=0; + if(pc_trait[0]!=-1) result+=TraitStatBonuses[statID*16+pc_trait[0]]; + if(pc_trait[1]!=-1) result+=TraitStatBonuses[statID*16+pc_trait[1]]; + switch(statID) { + case STAT_st: + if(check_trait(TRAIT_gifted)) result++; + if(check_trait(TRAIT_bruiser)) result+=2; + break; + case STAT_pe: + if(check_trait(TRAIT_gifted)) result++; + break; + case STAT_en: + if(check_trait(TRAIT_gifted)) result++; + break; + case STAT_ch: + if(check_trait(TRAIT_gifted)) result++; + break; + case STAT_iq: + if(check_trait(TRAIT_gifted)) result++; + break; + case STAT_ag: + if(check_trait(TRAIT_gifted)) result++; + if(check_trait(TRAIT_small_frame)) result++; + break; + case STAT_lu: + if(check_trait(TRAIT_gifted)) result++; + break; + case STAT_max_move_points: + if(check_trait(TRAIT_bruiser)) result-=2; + break; + case STAT_ac: + if(check_trait(TRAIT_kamikaze)) return -stat_get_base_direct(STAT_ac); + case STAT_melee_dmg: + if(check_trait(TRAIT_heavy_handed)) result+=2; + break; + case STAT_carry_amt: + if(check_trait(TRAIT_small_frame)) { + int str=stat_get_base_direct(STAT_st); + result-=str*10; + } + break; + case STAT_sequence: + if(check_trait(TRAIT_kamikaze)) result+=5; + break; + case STAT_heal_rate: + if(check_trait(TRAIT_fast_metabolism)) result+=2; + break; + case STAT_crit_chance: + if(check_trait(TRAIT_finesse)) result+=10; + break; + case STAT_better_crit: + if(check_trait(TRAIT_heavy_handed)) result-=30; + break; + case STAT_rad_resist: + if(check_trait(TRAIT_fast_metabolism)) return -stat_get_base_direct(STAT_rad_resist); + break; + case STAT_poison_resist: + if(check_trait(TRAIT_fast_metabolism)) return -stat_get_base_direct(STAT_poison_resist); + break; + } + return result; +} +static void __declspec(naked) TraitAdjustStatHook() { + __asm { + push edx; + push ecx; + push eax; + call trait_adjust_stat_override; + pop ecx; + pop edx; + retn; + } +} +static int _stdcall trait_adjust_skill_override(DWORD skillID) { + int result=0; + if(pc_trait[0]!=-1) result+=TraitSkillBonuses[skillID*16+pc_trait[0]]; + if(pc_trait[1]!=-1) result+=TraitSkillBonuses[skillID*16+pc_trait[1]]; + if(check_trait(TRAIT_gifted)) result-=10; + if(check_trait(TRAIT_good_natured)) { + if(skillID<=SKILL_THROWING) result-=10; + else if(skillID==SKILL_FIRST_AID||skillID==SKILL_DOCTOR||skillID==SKILL_CONVERSANT||skillID==SKILL_BARTER) result+=15; + } + return result; +} +static void __declspec(naked) TraitAdjustSkillHook() { + __asm { + push edx; + push ecx; + push eax; + call trait_adjust_skill_override; + pop ecx; + pop edx; + retn; + } +} +static void __declspec(naked) BlockedTrait() { + __asm { + xor eax, eax; + retn; + } +} +static void TraitSetup() { + MakeCall(0x4B3C7C, &TraitAdjustStatHook, true); + MakeCall(0x4B40FC, &TraitAdjustSkillHook, true); + + memset(tName, 0, sizeof(tName)); + memset(tDesc, 0, sizeof(tDesc)); + memcpy(Traits, (void*)0x51DB84, sizeof(TraitStruct)*16); + memset(TraitStatBonuses, 0, sizeof(TraitStatBonuses)); + memset(TraitSkillBonuses, 0, sizeof(TraitSkillBonuses)); + + SafeWrite32(0x4B3A81, (DWORD)Traits); + SafeWrite32(0x4B3B80, (DWORD)Traits); + SafeWrite32(0x4B3AAE, (DWORD)Traits + 4); + SafeWrite32(0x4B3BA0, (DWORD)Traits + 4); + SafeWrite32(0x4B3BC0, (DWORD)Traits + 8); + + if(strlen(perksFile)) { + char num[5], buf[512]; + num[0]='t'; + char* num2=&num[1]; + for(int i=0;i<16;i++) { + _itoa_s(i, num2, 4, 10); + if(GetPrivateProfileString(num, "Name", "", &tName[i*64], 63, perksFile)) Traits[i].Name=&tName[i*64]; + if(GetPrivateProfileString(num, "Desc", "", &tDesc[i*1024], 1023, perksFile)) { + Traits[i].Desc=&tDesc[i*1024]; + } + int value; + value=GetPrivateProfileInt(num, "Image", -99999, perksFile); + if(value!=-99999) Traits[i].Image=value; + + if(GetPrivateProfileStringA(num, "StatMod", "", buf, 512, perksFile)>0) { + char *stat, *mod; + stat=strtok(buf, "|"); + mod=strtok(0, "|"); + while(stat&&mod) { + int _stat=atoi(stat), _mod=atoi(mod); + if(_stat>=0&&_stat<=STAT_max_derived) TraitStatBonuses[_stat*16+i]=_mod; + stat=strtok(0, "|"); + mod=strtok(0, "|"); + } + } + + if(GetPrivateProfileStringA(num, "SkillMod", "", buf, 512, perksFile)>0) { + char *stat, *mod; + stat=strtok(buf, "|"); + mod=strtok(0, "|"); + while(stat&&mod) { + int _stat=atoi(stat), _mod=atoi(mod); + if(_stat>=0&&_stat<18) TraitSkillBonuses[_stat*16+i]=_mod; + stat=strtok(0, "|"); + mod=strtok(0, "|"); + } + } + + if(GetPrivateProfileInt(num, "NoHardcode", 0, perksFile)) { + disableTraits[i]=1; + switch(i) { + case 3: + HookCall(0x4245E0, &BlockedTrait); + break; + case 4: + HookCall(0x4248F9, &BlockedTrait); + break; + case 7: + HookCall(0x478C8A, &BlockedTrait); //fast shot + HookCall(0x478E70, &BlockedTrait); + break; + case 8: + HookCall(0x410707, &BlockedTrait); + break; + case 9: + HookCall(0x42389F, &BlockedTrait); + break; + case 11: + HookCall(0x47A0CD, &BlockedTrait); + HookCall(0x47A51A, &BlockedTrait); + break; + case 12: + HookCall(0x479BE1, &BlockedTrait); + HookCall(0x47A0DD, &BlockedTrait); + break; + case 14: + HookCall(0x43C295, &BlockedTrait); + HookCall(0x43C2F3, &BlockedTrait); + break; + case 15: + HookCall(0x43C2A4, &BlockedTrait); + break; + } + } + } + } + + for(int i=0;i<16;i++) { + if(Traits[i].Name!=&tName[64*i]) { + strcpy_s(&tName[64*i], 64, Traits[i].Name); + Traits[i].Name=&tName[64*i]; + } + if(Traits[i].Desc&&Traits[i].Desc!=&tDesc[1024*i]) { + strcpy_s(&tDesc[1024*i], 1024, Traits[i].Desc); + Traits[i].Desc=&tDesc[1024*i]; + } + } +} +static __declspec(naked) void PerkInitWrapper() { + __asm { + push ecx; + mov ecx, 0x4965A0; + call ecx; + pushad; + call PerkSetup; + popad; + pop ecx; + retn; + } +} +static __declspec(naked) void TraitInitWrapper() { + __asm { + push ecx; + mov ecx, 0x4B39F0; + call ecx; + pushad; + call TraitSetup; + popad; + pop ecx; + retn; + } +} + +void _stdcall SetPerkValue(int id, int value, DWORD offset) { + if(id<0||id>=119) return; + *(DWORD*)((DWORD)(&Perks[id])+offset)=value; +} +void _stdcall SetPerkName(int id, char* value) { + if(id<0||id>=119) return; + strcpy_s(&Name[id*64], 64, value); +} +void _stdcall SetPerkDesc(int id, char* value) { + if(id<0||id>=119) return; + strcpy_s(&Desc[id*1024], 1024, value); + Perks[id].Desc=&Desc[1024*id]; +} +void PerksInit() { + HookCall(0x442729, &PerkInitWrapper); + if(GetPrivateProfileString("Misc", "PerksFile", "", &perksFile[2], 257, ini)) { + perksFile[0]='.'; + perksFile[1]='\\'; + HookCall(0x44272E, &TraitInitWrapper); + } else perksFile[0]=0; +} +void PerksReset() { + fakeTraits.clear(); + fakePerks.clear(); + fakeSelectablePerks.clear(); + SafeWrite32(0x0043C77D, 0x488cb); + IgnoringDefaultPerks=0; + addPerkMode=2; + SafeWrite8(0x478AC4, 0xba); + SafeWrite32(0x478AC5, 0x23); + PerkFreqOverride=0; +} +void PerksSave(HANDLE file) { + DWORD count; + DWORD unused; + count=fakeTraits.size(); + WriteFile(file, &count, 4, &unused, 0); + for(DWORD i=0;i. + */ + +#pragma once + +void PerksInit(); +void PerksReset(); +void PerksSave(HANDLE file); +bool PerksLoad(HANDLE file); + +void PerksEnterCharScreen(); +void PerksCancelCharScreen(); +void PerksAcceptCharScreen(); + +void _stdcall ApplyHeaveHoFix(); + +void _stdcall SetPerkValue(int id, int value, DWORD offset); +void _stdcall SetPerkName(int id, char* value); +void _stdcall SetPerkDesc(int id, char* value); + +void _stdcall SetPerkboxTitle(char* title); +void _stdcall SetSelectablePerk(char* name, int level, int image, char* desc); +void _stdcall SetFakePerk(char* name, int level, int image, char* desc); +void _stdcall SetFakeTrait(char* name, int level, int image, char* desc); +void _stdcall IgnoreDefaultPerks(); +void _stdcall RestoreDefaultPerks(); + +void _stdcall AddPerkMode(DWORD mode); +DWORD _stdcall HasFakePerk(char* name); +DWORD _stdcall HasFakeTrait(char* name); +void _stdcall ClearSelectablePerks(); + +void _stdcall SetPerkFreq(int i); \ No newline at end of file diff --git a/sfall/Premade.cpp b/sfall/Premade.cpp new file mode 100644 index 00000000..9cba0dcf --- /dev/null +++ b/sfall/Premade.cpp @@ -0,0 +1,62 @@ +/* + * sfall + * Copyright (C) 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +struct PremadeChar { + char path[20]; + DWORD fid; + char unknown[20]; +}; + +PremadeChar* premade; + +void PremadeInit() { + char buf[512]; + GetPrivateProfileString("misc", "PremadePaths", "", buf, 512, ini); + if(buf[0]) { + char buf2[512]; + GetPrivateProfileString("misc", "PremadeFIDs", "", buf2, 512, ini); + + int count=1; + char* tmp=buf; + while(tmp=strchr(tmp, ',')) { tmp++; count++; } + premade=new PremadeChar[count]; + + tmp=buf; + char* tmp2=buf2; + for(int i=0;i. + */ + +void PremadeInit(); \ No newline at end of file diff --git a/sfall/QuestList.cpp b/sfall/QuestList.cpp new file mode 100644 index 00000000..b89b012f --- /dev/null +++ b/sfall/QuestList.cpp @@ -0,0 +1,375 @@ +/* + * sfall + * Copyright (C) 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +static DWORD calledflag=0x0; +static DWORD called_quest_number=0x0; +static DWORD total_quests=0x0; +static DWORD curent_quest_page=0x0; +static DWORD wait_flag=0x0; + +static void __declspec(naked) newhookpress() { + __asm { + push eax; + mov calledflag, 1; + mov called_quest_number, ebx; + mov wait_flag, 0; + mov total_quests, 0; + pop eax; + mov edx,ds:[0x664508]; + retn; + } +} + +static const DWORD back_function=0x497828; + +static void __declspec(naked) backhookfunct() { + __asm { + push eax; + mov calledflag, 0; + mov curent_quest_page, 0; + pop eax; + call back_function; + retn; + } +} + +static void __declspec(naked) newhookpress1() { + __asm { + push eax; + mov calledflag, 0; + mov curent_quest_page, 0; + pop eax; + sub ebx,0x1f4; + retn; + } +} +static void __declspec(naked) nexthookfunct() { + __asm { + push eax; + mov calledflag, 0; + mov curent_quest_page, 0; + pop eax; + mov eax,0x401; + retn; + } +} + +static const DWORD show_text_function=0x4bc6f0; +static const DWORD show_text_jump_addr=0x498193; + +static void __declspec(naked) newhooklooktext() { + __asm { + push eax; + mov eax,curent_quest_page; + imul eax,eax,0x9; + cmp wait_flag,eax; +jl smpfhj; + add eax, 0x9; + cmp wait_flag,eax; + jg smpfhj; + pop eax; + call show_text_function; + inc wait_flag; + add total_quests, 1; + retn; +smpfhj: + pop eax; + add total_quests, 1; + inc wait_flag; + mov eax, 1; + retn; + } +} +static const DWORD forresetvalue=0x497b64; +static void __declspec(naked) newhookresetvalue() { + __asm { + push eax; + mov wait_flag,0; + pop eax; + call forresetvalue; + retn; + } +} + +static const DWORD call_newbutt1=0x49720c; +static const DWORD call_newbutt2=0x497BD8; + +static const DWORD call_newbutt3=0x4999c0; +static const DWORD call_newbutt4=0x4d36d4; + +static void __declspec(naked) newactbpip() { + __asm { + cmp ebx, 0x300; + je jmphr4; + jmp jmphr3; +jmphr4: + cmp calledflag, 0x1; + je jmphr5; + jmp jmphr3; +jmphr5: + + cmp curent_quest_page, 0x0; + je jmphr1; + + push eax; + sub curent_quest_page,1; + mov wait_flag, 0; + pop eax; + mov ebx, called_quest_number; +jmphr3: + + cmp ebx, 0x301; + je jmphr; + jmp jmphr1; +jmphr: + + cmp calledflag, 0x1; + je jmphr2; + jmp jmphr1; +jmphr2: + + push eax; + mov eax, curent_quest_page; + add eax, 1; + imul eax,eax,0x9; + cmp eax, total_quests; + jl rtjl; + pop eax; + jmp jmphr1; +rtjl: + add curent_quest_page,1; + mov wait_flag, 0; + pop eax; + mov ebx, called_quest_number; +jmphr1: + cmp ebx,0x1f4; + retn; + } +} +static const DWORD n_newbutt1=0x4d8260; +static const DWORD n_newbutt2=0x4d87f8; + +static const DWORD newbutt1=0x4d87f8; +static const DWORD newbutt2=0x419c88; +static const DWORD newbutt3=0x419188; +static const DWORD newbutt4=0x45e440; +static const DWORD newbutt5=0x419c88; +static const DWORD newbutt6=0x419188; +static const DWORD newbutt7=0x4d8260; +static const DWORD newbutt8=0x45e440; +static const DWORD newbutt9=0x45e440; + +static void __declspec(naked) newbuttonfct() { + __asm { + + // recreate original 4 buttons + mov edi,0x155; + mov ebp,0x1f4; + xor esi,esi; +smpj1: + cmp esi,+0x1; + jz smpj4; + push +0x20; + push +0x0; + mov eax,ds:[0x664424]; + push eax; + mov edx,ds:[0x664420]; + push edx; + push ebp; + push -0x1; + push -0x1; + mov ebx,ds:[0x6642e4]; + push -0x1; + mov ecx,ds:[0x6642e0]; + mov eax,ds:[0x6644c4]; + push ebx; + mov edx,0x35; + mov ebx,edi; + call n_newbutt1; + cmp eax,-0x1; + jz smpj3; + mov ebx,0x451978; + mov edx,0x451970; + call n_newbutt2; +smpj3: + inc ebp; +smpj4: + inc esi; + add edi,+0x1b; + cmp esi,+0x5; + jl smpj1; + + + // load new texture for first (up) button. I used memory address for texture from buttons + // at chracter screen. Everything fine, because this buttons can't use in one time, and + // they everytime recreating + mov ebx,0x451990; + mov edx,0x451988; + call newbutt1; + xor ecx,ecx; + push +0x0; + mov edx,0xB5; //number from intrface.lst for button-up + mov eax,0x6; + xor ebx,ebx; + call newbutt2; + mov ecx,0x518f28; + xor ebx,ebx; + xor edx,edx; + call newbutt3; + mov ds:[0x59d3fc],eax; // first texture memory address + push +0x0; + mov edx,0xB6; //number from intrface.lst for button-down + mov eax,0x6; + xor ecx,ecx; + xor ebx,ebx; + call newbutt5; + mov ecx,0x518f2c; + xor ebx,ebx; + xor edx,edx; + call newbutt6; + mov ds:[0x59d400],eax; // second texture memory address + + + // load new texture for second (down) button + + mov ebx,0x451990; + mov edx,0x451988; + call newbutt1; + xor ecx,ecx; + push +0x0; + mov edx,0xC7; //number from intrface.lst for button-up + mov eax,0x6; + xor ebx,ebx; + call newbutt2; + mov ecx,0x518f28; + xor ebx,ebx; + xor edx,edx; + call newbutt3; + mov ds:[0x570514],eax; // first texture memory address + push +0x0; + mov edx,0xC8; //number from intrface.lst for button-down + mov eax,0x6; + xor ecx,ecx; + xor ebx,ebx; + call newbutt5; + mov ecx,0x518f2c; + xor ebx,ebx; + xor edx,edx; + call newbutt6; + mov ds:[0x570518],eax; // second texture memory address + + + // creating new 2 buttons + + mov edi,0x149; // y position + mov ebp,0x300; // this number will return when button pressed + xor esi,esi; + + push +0x20; + push +0x0; + mov eax,ds:[0x570518]; + push eax; + mov edx,ds:[0x570514]; + push edx; + push ebp; + push -0x1; + push -0x1; + mov ebx,0xE; + push -0x1; + mov ecx,0xB; + mov eax,ds:[0x6644c4]; + push ebx; + mov edx,0x8B; + mov ebx,edi; + call n_newbutt1; + cmp eax,-0x1; + jz smpj16; + mov ebx,0x451978; + mov edx,0x451970; + call n_newbutt2; +smpj16: + // second button + inc ebp; + inc esi; + add edi,+0x9; + + push +0x20; + push +0x0; + mov eax,ds:[0x59d400]; + push eax; + mov edx,ds:[0x59d3fc]; + push edx; + push ebp; + push -0x1; + push -0x1; + mov ebx,0xE; + push -0x1; + mov ecx,0xB; + mov eax,ds:[0x6644c4]; + push ebx; + mov edx,0x8B; + mov ebx,edi; + call n_newbutt1; + cmp eax,-0x1; + jz smpj6; + mov ebx,0x451978; + mov edx,0x451970; + call n_newbutt2; +smpj6: + inc ebp; + inc esi; + add edi,+0x9; + + + retn; + } +} + +void QuestListInit() { +// + SafeWrite8(0x004974df, 0xE8); + SafeWrite32(0x004974e0, ((DWORD)&newbuttonfct) - 0x004974e4); + SafeWrite8(0x004974e4, 0xE9); + SafeWrite32(0x004974e5, 0x0000005B); +// + SafeWrite8(0x00497173, 0xE8); + SafeWrite32(0x00497174, ((DWORD)&newactbpip) - 0x00497178); + SafeWrite8(0x00497178, 0x90); +// + SafeWrite8(0x004971b2, 0xE8); + SafeWrite32(0x004971b3, ((DWORD)&newhookpress) - 0x004971b7); + SafeWrite8(0x004971b7, 0x90); +// + SafeWrite8(0x00497183, 0xE8); + SafeWrite32(0x00497184, ((DWORD)&newhookpress1) - 0x00497188); + SafeWrite8(0x00497188, 0x90); +// + SafeWrite8(0x00498186, 0xE8); + SafeWrite32(0x00498187, ((DWORD)&newhooklooktext) - 0x0049818B); +// + SafeWrite8(0x004982B0, 0xE8); + SafeWrite32(0x004982B1, ((DWORD)&newhookresetvalue) - 0x004982B5); +// + SafeWrite8(0x004971d9, 0xE8); + SafeWrite32(0x004971da, ((DWORD)&nexthookfunct) - 0x004971de); +// + SafeWrite8(0x00497219, 0xE8); + SafeWrite32(0x0049721a, ((DWORD)&backhookfunct) - 0x0049721e); +} \ No newline at end of file diff --git a/sfall/QuestList.h b/sfall/QuestList.h new file mode 100644 index 00000000..516346d2 --- /dev/null +++ b/sfall/QuestList.h @@ -0,0 +1,19 @@ +/* + * sfall + * Copyright (C) 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +void QuestListInit(); \ No newline at end of file diff --git a/sfall/Reputations.cpp b/sfall/Reputations.cpp new file mode 100644 index 00000000..83b23def --- /dev/null +++ b/sfall/Reputations.cpp @@ -0,0 +1,56 @@ +/* + * sfall + * Copyright (C) 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +struct CityRep { + DWORD globalID; + DWORD cityID; +}; + +static CityRep* repList; + +void ReputationsInit() { + int count; + if(count=GetPrivateProfileIntA("Misc", "CityRepsCount", 0, ini)) { + repList=new CityRep[count]; + char buf[512]; + GetPrivateProfileStringA("Misc", "CityRepsList", "", buf, 512, ini); + char* end; + char* start=buf; + for(int i=0;i void _stdcall SafeWrite(DWORD addr, T data) { + DWORD oldProtect; + VirtualProtect((void *)addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect); + *((T*)addr) = data; + VirtualProtect((void *)addr, 1, oldProtect, &oldProtect); +} +void _stdcall SafeWrite8(DWORD addr, BYTE data); +void _stdcall SafeWrite16(DWORD addr, WORD data); +void _stdcall SafeWrite32(DWORD addr, DWORD data); +void _stdcall SafeWriteStr(DWORD addr, const char* data); +void HookCall(DWORD addr, void* func); +void MakeCall(DWORD addr, void* func, bool jump); +void BlockCall(DWORD addr); +void SafeMemSet(DWORD addr, BYTE val, int len); +void SafeWriteBytes(DWORD addr, BYTE* data, int count); \ No newline at end of file diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp new file mode 100644 index 00000000..6122c09b --- /dev/null +++ b/sfall/ScriptExtender.cpp @@ -0,0 +1,1568 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2011, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "main.h" +#include "FalloutEngine.h" +#include "ScriptExtender.h" +#include "Arrays.h" +#include "HookScripts.h" +#include "input.h" +#include "LoadGameHook.h" +#include "version.h" +#include "numbers.h" +#include "Logging.h" +#include "BarBoxes.h" +#include "Console.h" +#include "Explosions.h" + +void _stdcall HandleMapUpdateForScripts(DWORD procId); + +// variables for new opcodes +static DWORD opArgCount = 0; +static DWORD opArgs[5]; +static DWORD opArgTypes[5]; +static DWORD opRet; +static DWORD opRetType; + +static void _stdcall SetOpReturn(DWORD value, DWORD type) { + opRet = value; + opRetType = type; +} + +static void _stdcall SetOpReturn(int value) { + SetOpReturn((DWORD)value, DATATYPE_INT); +} + +static void _stdcall SetOpReturn(float value) { + SetOpReturn(*(DWORD*)&value, DATATYPE_FLOAT); +} + +static void _stdcall SetOpReturn(const char* value) { + SetOpReturn((DWORD)value, DATATYPE_STR); +} + +static bool _stdcall IsOpArgInt(int num) { + return (opArgTypes[num] == DATATYPE_INT); +} + +static bool _stdcall IsOpArgFloat(int num) { + return (opArgTypes[num] == DATATYPE_FLOAT); +} + +static bool _stdcall IsOpArgStr(int num) { + return (opArgTypes[num] == DATATYPE_STR); +} + +static int _stdcall GetOpArgInt(int num) { + switch (opArgTypes[num]) { + case DATATYPE_FLOAT: + return (int)*(float*)&opArgs[num]; + case DATATYPE_INT: + return (int)opArgs[num]; + default: + return 0; + } +} + +static float _stdcall GetOpArgFloat(int num) { + switch (opArgTypes[num]) { + case DATATYPE_FLOAT: + return *(float*)&opArgs[num]; + case DATATYPE_INT: + return (float)(int)opArgs[num]; + default: + return 0.0; + } +} + +static const char* _stdcall GetOpArgStr(int num) { + return (opArgTypes[num] == DATATYPE_STR) + ? (const char*)opArgs[num] + : ""; +} + +// common useful functions offsets, related to scripting engine +static const DWORD exec_script_proc_ = 0x4A4810; // unsigned int aScriptID, int aProcId +static const DWORD scr_find_obj_from_program_ = 0x4A39AC; // eax - *program - finds self_obj by program pointer (has nice additional effect - creates fake object for a spatial script) +static const DWORD loadProgram_ = 0x4A3B74; // loads script from scripts/ folder by file name and returns pointer to it: char* - file name (w/o extension) +static const DWORD interpret_ = 0x46CCA4; // - programPtr, - ??? (-1) +static const DWORD interpreFindProcedure_ = 0x46DCD0; // get proc number (different for each script) by name: * - scriptPtr, char* - proc name +static const DWORD runProgram_ = 0x46E154; // eax - programPtr, called once for each program after first loaded - hooks program to game and UI events +static const DWORD _procTableStrs = 0x51C758; // table of procId (from define.h) => procName map +static const DWORD runProcedure_ = 0x46DD2C; // - programPtr, - procNumber +static const DWORD interpretFreeProgram_ = 0x467614; // - program ptr, frees it from memory and from scripting engine + +#include "ScriptOps\AsmMacros.h" +#include "ScriptOps\ScriptArrays.hpp" +#include "ScriptOps\ScriptUtils.hpp" + +#include "ScriptOps\WorldmapOps.hpp" +#include "ScriptOps\PerksOp.hpp" +#include "ScriptOps\MemoryOp.hpp" +#include "ScriptOps\StatsOp.hpp" +#include "ScriptOps\InterfaceOp.hpp" +#include "ScriptOps\GraphicsOp.hpp" +#include "ScriptOps\FileSystemOps.hpp" +#include "ScriptOps\ObjectsOps.hpp" +#include "ScriptOps\AnimOps.hpp" +#include "ScriptOps\MiscOps.hpp" + + +typedef void (_stdcall *regOpcodeProc)(WORD opcode,void* ptr); + +static DWORD highlightingToggled=0; +static DWORD MotionSensorMode; +static BYTE toggleHighlightsKey; +static int idle; +static char HighlightFail1[128]; +static char HighlightFail2[128]; + +struct sGlobalScript { + sScriptProgram prog; + DWORD count; + DWORD repeat; + DWORD mode; //0 - local map loop, 1 - input loop, 2 - world map loop, 3 - local and world map loops + + sGlobalScript() {} + sGlobalScript(sScriptProgram script) { + prog=script; + count=0; + repeat=0; + mode=0; + } +}; + +struct sExportedVar { + DWORD type; // in scripting engine terms, eg. VAR_TYPE_* + DWORD val; + sExportedVar() : val(0), type(VAR_TYPE_INT) {} +}; + +static std::vector checkedScripts; +static std::vector globalScripts; +// a map of all sfall programs (global and hook scripts) by thier scriptPtr +typedef stdext::hash_map SfallProgsMap; +static SfallProgsMap sfallProgsMap; +// a map scriptPtr => self_obj to override self_obj for all script types using set_self +stdext::hash_map selfOverrideMap; + +typedef std::tr1::unordered_map ExportedVarsMap; +static ExportedVarsMap globalExportedVars; +DWORD isGlobalScriptLoading = 0; + +stdext::hash_map<__int64, int> globalVars; +typedef stdext::hash_map<__int64, int> :: iterator glob_itr; +typedef stdext::hash_map<__int64, int> :: const_iterator glob_citr; +typedef std::pair<__int64, int> glob_pair; + +static void* opcodes[0x300]; +DWORD AddUnarmedStatToGetYear=0; +DWORD AvailableGlobalScriptTypes=0; +bool isGameLoading; + +TScript OverrideScriptStruct; + +static TGameObj** obj_dude_ptr = (TGameObj**)0x6610B8; + +//eax contains the script pointer, edx contains the opcode*4 + +//To read a value, mov the script pointer to eax, call 0x4674F0, eax now contains the value type +//mov the script pointer to eax, call 0x467500, eax now contains the value + +//To return a value, move it to edx, mov the script pointer to eax, call 0x4674DC +//mov the value type to edx, mov the script pointer to eax, call 0x46748C + + + +static void _stdcall SetGlobalScriptRepeat2(DWORD script, DWORD frames) { + for(DWORD d=0;d3) return; + for(DWORD d=0;dsecond=val; + } +} +static void _stdcall SetGlobalVar2(const char* var, int val) { + if(strlen(var)!=8) return; + SetGlobalVarInternal(*(__int64*)var, val); +} +static void _stdcall SetGlobalVar2Int(DWORD var, int val) { + SetGlobalVarInternal(var, val); +} +static void __declspec(naked) SetGlobalVar() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + mov esi, eax; + mov eax, edi; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jz next; + cmp dx, 0xc001; + jnz end; + push esi; + push eax; + call SetGlobalVar2Int; + jmp end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push esi; + push eax; + call SetGlobalVar2; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static DWORD GetGlobalVarInternal(__int64 val) { + glob_citr itr=globalVars.find(val); + if(itr==globalVars.end()) return 0; + else return itr->second; +} +static DWORD _stdcall GetGlobalVar2(const char* var) { + if(strlen(var)!=8) return 0; + return GetGlobalVarInternal(*(__int64*)var); +} +static DWORD _stdcall GetGlobalVar2Int(DWORD var) { + return GetGlobalVarInternal(var); +} +static void __declspec(naked) GetGlobalVarInt() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + xor edx, edx; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp si, 0x9001; + jz next; + cmp si, 0x9801; + jz next; + cmp si, 0xc001; + jnz end; + push eax; + call GetGlobalVar2Int; + mov edx, eax; + jmp end; +next: + mov edx, esi; + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push eax; + call GetGlobalVar2; + mov edx, eax; +end: + mov eax, edi; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, edi; + mov ebx, 0x46748C; + call ebx; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetGlobalVarFloat() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + xor edx, edx; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp si, 0x9001; + jz next; + cmp si, 0x9801; + jz next; + cmp si, 0xc001; + jnz end; + push eax; + call GetGlobalVar2Int; + mov edx, eax; + jmp end; +next: + mov edx, esi; + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push eax; + call GetGlobalVar2; + mov edx, eax; +end: + mov eax, edi; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xa001; + mov eax, edi; + mov ebx, 0x46748C; + call ebx; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + + + +static void __declspec(naked) GetSfallArg() { + __asm { + pushad; + push eax; + call GetHSArg; + pop ecx; + mov edx, eax; + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static DWORD _stdcall GetSfallArgs2() { + DWORD argCount=GetHSArgCount(); + DWORD id=TempArray(argCount, 4); + DWORD* args = GetHSArgs(); + for(DWORD i=0;i::iterator it = selfOverrideMap.find(script); + if (it != selfOverrideMap.end()) + selfOverrideMap.erase(it); + } +} + +static void __declspec(naked) set_self() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + push eax; + push ebp; + call set_self2; +end: + popad; + retn; + } +} +static void __declspec(naked) register_hook() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + push -1; + push eax; + push ebp; + call RegisterHook; +end: + popad; + retn; + } +} + +static void __declspec(naked) register_hook_proc() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ecx, esi) + _GET_ARG_R32(ebp, ebx, edi) + _CHECK_ARG_INT(cx, fail) + _CHECK_ARG_INT(bx, fail) + __asm { + push esi; + push edi; + push ebp; + call RegisterHook; + fail: + } + _OP_END +} + +static void __declspec(naked) sfall_ver_major() { + __asm { + push edx; + push ecx; + mov edx, VERSION_MAJOR; + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop ecx; + pop edx; + retn; + } +} +static void __declspec(naked) sfall_ver_minor() { + __asm { + push edx; + push ecx; + mov edx, VERSION_MINOR; + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop ecx; + pop edx; + retn; + } +} +static void __declspec(naked) sfall_ver_build() { + __asm { + push edx; + push ecx; + mov edx, VERSION_BUILD; + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop ecx; + pop edx; + retn; + } +} + + +//END OF SCRIPT FUNCTIONS +static const DWORD scr_find_sid_from_program=0x4A390C + 5; +static const DWORD scr_ptr=0x4A5E34 + 5; +static const DWORD scr_find_obj_from_program=0x4A39AC + 7; + +DWORD _stdcall FindSidHook2(DWORD script) { + stdext::hash_map::iterator overrideIt = selfOverrideMap.find(script); + if (overrideIt != selfOverrideMap.end()) { + DWORD scriptId = overrideIt->second->scriptID; + if (scriptId != -1) { + selfOverrideMap.erase(overrideIt); + return scriptId; // returns the real scriptId of object if it is scripted + } + OverrideScriptStruct.self_obj = overrideIt->second; + OverrideScriptStruct.target_obj = overrideIt->second; + selfOverrideMap.erase(overrideIt); // this reverts self_obj back to original value for next function calls + return -2; // override struct + } + // this will allow to use functions like roll_vs_skill, etc without calling set_self (they don't really need self object) + if (sfallProgsMap.find(script) != sfallProgsMap.end()) { + OverrideScriptStruct.target_obj = OverrideScriptStruct.self_obj = 0; + return -2; // override struct + } + return -1; // change nothing +} + +static void __declspec(naked) FindSidHook() { + __asm { + sub esp, 4; + pushad; + push eax; + call FindSidHook2; + mov [esp+32], eax; // to save value after popad + popad; + add esp, 4; + cmp [esp-4], -1; + jz end; + cmp [esp-4], -2; + jz override_script; + mov eax, [esp-4]; + retn; +override_script: + test edx, edx; + jz end; + lea eax, OverrideScriptStruct; + mov [edx], eax; + mov eax, -2; + retn; +end: + push ebx; + push ecx; + push edx; + push esi; + push ebp; + jmp scr_find_sid_from_program; + } +} +static void __declspec(naked) ScrPtrHook() { + __asm { + cmp eax, -2; + jnz end; + xor eax, eax; + retn; +end: + push ebx; + push ecx; + push esi; + push edi; + push ebp; + jmp scr_ptr; + } +} + +static void __declspec(naked) MainGameLoopHook() { + __asm { + push ebx; + push ecx; + push edx; + mov ebx, 0x004C8B78; + call ebx; + push eax; + call RunGlobalScripts1; + pop eax; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) CombatLoopHook() { + __asm { + pushad; + call RunGlobalScripts1; + popad; + mov ebx, 0x004C8B78; + jmp ebx; + } +} +static void __declspec(naked) AfterCombatAttackHook() { + __asm { + pushad; + call AfterAttackCleanup; + popad; + mov eax, 1; + mov edx, 0x4230DA; + jmp edx; + } +} +static void __declspec(naked) ExecMapScriptsHook() { + __asm { + sub esp, 32; + mov [esp+12], eax; + pushad; + push eax; // int procId + call HandleMapUpdateForScripts; + popad; + mov ebx, 0x4A67F9; // jump back + jmp ebx; + } +} +static DWORD __stdcall GetGlobalExportedVarPtr(const char* name) { + std::string str(name); + ExportedVarsMap::iterator it = globalExportedVars.find(str); + //dlog_f("\n Trying to find exported var %s... ", DL_MAIN, name); + if (it != globalExportedVars.end()) { + sExportedVar *ptr = &it->second; + return (DWORD)ptr; + } + return 0; +} +static DWORD __stdcall CreateGlobalExportedVar(DWORD scr, const char* name) { + //dlog_f("\nTrying to export variable %s (%d)\r\n", DL_MAIN, name, isGlobalScriptLoading); + std::string str(name); + globalExportedVars[str] = sExportedVar(); // add new + return 1; +} +static const DWORD findVar_ = 0x4410AC; +/* + when fetching/storing into exported variables, first search in our own hash table instead, then (if not found), resume normal search + + reason for this: game frees all scripts and exported variables from memory when you exit map + so if you create exported variable in sfall global script, it will work until you exit map, after that you will get crashes + + with this you can still use variables exported from global scripts even between map changes (per global scripts logic) +*/ +static void __declspec(naked) Export_FetchOrStore_FindVar_Hook() { + __asm { + push eax; + push edx; + push ecx; + push ebx; + push edx // char* varName + call GetGlobalExportedVarPtr; + test eax, eax + jz proceedNormal; + pop ebx; + pop ecx; + pop edx; + pop edx; + sub eax, 0x24; // adjust pointer for the code + retn; + + proceedNormal: + pop ebx; + pop ecx; + pop edx; + pop eax; + call findVar_ + retn + } +} +static const DWORD Export_Export_FindVar_back1 = 0x4414CD; +static const DWORD Export_Export_FindVar_back2 = 0x4414AC; +static void __declspec(naked) Export_Export_FindVar_Hook() { + __asm { + pushad; + mov eax, isGlobalScriptLoading; + test eax, eax; + jz proceedNormal; + push edx; // char* - var name + push ebp; // script ptr + call CreateGlobalExportedVar; + popad; + jmp Export_Export_FindVar_back2; // if sfall exported var, jump to the end of function + +proceedNormal: + popad; + call findVar_; // else - proceed normal + jmp Export_Export_FindVar_back1; + } +} + +// this hook prevents sfall scripts from being removed after switching to another map, since normal script engine re-loads completely +static void _stdcall FreeProgramHook2(DWORD progPtr) { + if (isGameLoading || (sfallProgsMap.find(progPtr) == sfallProgsMap.end())) { // only delete non-sfall scripts or when actually loading the game + __asm { + mov eax, progPtr; + call interpretFreeProgram_; + } + } +} +static void __declspec(naked) FreeProgramHook() { + __asm { + pushad; + push eax; + call FreeProgramHook2; + popad; + retn; + } +} + + +void ScriptExtenderSetup() { +#ifdef TRACE + bool AllowUnsafeScripting=GetPrivateProfileIntA("Debugging", "AllowUnsafeScripting", 0, ini)!=0; +#else + const bool AllowUnsafeScripting=false; +#endif + toggleHighlightsKey=GetPrivateProfileIntA("Input", "ToggleItemHighlightsKey", 0, ini); + if(toggleHighlightsKey) MotionSensorMode=GetPrivateProfileIntA("Misc", "MotionScannerFlags", 1, ini); + GetPrivateProfileStringA("Sfall", "HighlightFail1", "You aren't carrying a motion sensor.", HighlightFail1, 128, translationIni); + GetPrivateProfileStringA("Sfall", "HighlightFail2", "Your motion sensor is out of charge.", HighlightFail2, 128, translationIni); + + idle=GetPrivateProfileIntA("Misc", "ProcessorIdle", -1, ini); + modifiedIni=GetPrivateProfileIntA("Main", "ModifiedIni", 0, ini); + + dlogr("Adding additional opcodes", DL_SCRIPT); + if(AllowUnsafeScripting) dlogr(" Unsafe opcodes enabled", DL_SCRIPT); + else dlogr(" Unsafe opcodes disabled", DL_SCRIPT); + + arraysBehavior = GetPrivateProfileIntA("Misc", "arraysBehavior", 1, ini); + if (arraysBehavior > 0) { + arraysBehavior = 1; // only 1 and 0 allowed at this time + dlogr("New arrays behavior enabled.", DL_SCRIPT); + } else dlogr("Arrays in backward-compatiblity mode.", DL_SCRIPT); + + SafeWrite32(0x46E370, 0x300); //Maximum number of allowed opcodes + SafeWrite32(0x46ce34, (DWORD)opcodes); //cmp check to make sure opcode exists + SafeWrite32(0x46ce6c, (DWORD)opcodes); //call that actually jumps to the opcode + SafeWrite32(0x46e390, (DWORD)opcodes); //mov that writes to the opcode + + HookCall(0x480E7B, MainGameLoopHook); //hook the main game loop + HookCall(0x422845, CombatLoopHook); //hook the combat loop + + MakeCall(0x4A390C, &FindSidHook, true); + MakeCall(0x4A5E34, &ScrPtrHook, true); + memset(&OverrideScriptStruct, 0, sizeof(TScript)); + + MakeCall(0x4230D5, &AfterCombatAttackHook, true); + MakeCall(0x4A67F2, &ExecMapScriptsHook, true); + + // this patch makes it possible to export variables from sfall global scripts + MakeCall(0x4414C8, &Export_Export_FindVar_Hook, true); + HookCall(0x441285, &Export_FetchOrStore_FindVar_Hook); // store + HookCall(0x4413D9, &Export_FetchOrStore_FindVar_Hook); // fetch + + // fix vanilla negate operator on float values + MakeCall(0x46AB63, &NegateFixHook, true); + // fix incorrect int-to-float conversion + // op_mult: + SafeWrite16(0x46A3F4, 0x04DB); // replace operator to "fild 32bit" + SafeWrite16(0x46A3A8, 0x04DB); + // op_div: + SafeWrite16(0x46A566, 0x04DB); + SafeWrite16(0x46A4E7, 0x04DB); + + HookCall(0x46E141, FreeProgramHook); + + if(AllowUnsafeScripting) { + opcodes[0x156]=ReadByte; + opcodes[0x157]=ReadShort; + opcodes[0x158]=ReadInt; + opcodes[0x159]=ReadString; + } + opcodes[0x15a]=SetPCBaseStat; + opcodes[0x15b]=SetPCExtraStat; + opcodes[0x15c]=GetPCBaseStat; + opcodes[0x15d]=GetPCExtraStat; + opcodes[0x15e]=SetCritterBaseStat; + opcodes[0x15f]=SetCritterExtraStat; + opcodes[0x160]=GetCritterBaseStat; + opcodes[0x161]=GetCritterExtraStat; + opcodes[0x162]=funcTapKey; + opcodes[0x163]=GetYear; + opcodes[0x164]=GameLoaded; + opcodes[0x165]=GraphicsFuncsAvailable; + opcodes[0x166]=funcLoadShader; + opcodes[0x167]=funcFreeShader; + opcodes[0x168]=funcActivateShader; + opcodes[0x169]=funcDeactivateShader; + opcodes[0x16a]=SetGlobalScriptRepeat; + opcodes[0x16b]=InputFuncsAvailable; + opcodes[0x16c]=KeyPressed; + opcodes[0x16d]=funcSetShaderInt; + opcodes[0x16e]=funcSetShaderFloat; + opcodes[0x16f]=funcSetShaderVector; + opcodes[0x170]=funcInWorldMap; + opcodes[0x171]=ForceEncounter; + opcodes[0x172]=SetWorldMapPos; + opcodes[0x173]=GetWorldMapXPos; + opcodes[0x174]=GetWorldMapYPos; + opcodes[0x175]=SetDMModel; + opcodes[0x176]=SetDFModel; + opcodes[0x177]=SetMoviePath; + for(int i=0x178;i<0x189;i++) + opcodes[i]=funcSetPerkValue; + opcodes[0x189]=funcSetPerkName; + opcodes[0x18a]=funcSetPerkDesc; + opcodes[0x18b]=SetPipBoyAvailable; + if(UsingExtraKillTypes()) { + opcodes[0x18c]=GetKillCounter2; + opcodes[0x18d]=ModKillCounter2; + } else { + opcodes[0x18c]=GetKillCounter; + opcodes[0x18d]=ModKillCounter; + } + opcodes[0x18e]=GetPerkOwed; + opcodes[0x18f]=SetPerkOwed; + opcodes[0x190]=GetPerkAvailable; + opcodes[0x191]=GetCritterAP; + opcodes[0x192]=SetCritterAP; + opcodes[0x193]=GetActiveHand; + opcodes[0x194]=ToggleActiveHand; + opcodes[0x195]=SetWeaponKnockback; + opcodes[0x196]=SetTargetKnockback; + opcodes[0x197]=SetAttackerKnockback; + opcodes[0x198]=RemoveWeaponKnockback; + opcodes[0x199]=RemoveTargetKnockback; + opcodes[0x19a]=RemoveAttackerKnockback; + opcodes[0x19b]=SetGlobalScriptType; + opcodes[0x19c]=GetGlobalScriptTypes; + opcodes[0x19d]=SetGlobalVar; + opcodes[0x19e]=GetGlobalVarInt; + opcodes[0x19f]=GetGlobalVarFloat; + opcodes[0x1a0]=fSetPickpocketMax; + opcodes[0x1a1]=fSetHitChanceMax; + opcodes[0x1a2]=fSetSkillMax; + opcodes[0x1a3]=EaxAvailable; + //opcodes[0x1a4]=SetEaxEnvironmentFunc; + opcodes[0x1a5]=IncNPCLevel; + opcodes[0x1a6]=GetViewportX; + opcodes[0x1a7]=GetViewportY; + opcodes[0x1a8]=SetViewportX; + opcodes[0x1a9]=SetViewportY; + opcodes[0x1aa]=SetXpMod; + opcodes[0x1ab]=SetPerkLevelMod; + opcodes[0x1ac]=GetIniSetting; + opcodes[0x1ad]=funcGetShaderVersion; + opcodes[0x1ae]=funcSetShaderMode; + opcodes[0x1af]=GetGameMode; + opcodes[0x1b0]=funcForceGraphicsRefresh; + opcodes[0x1b1]=funcGetShaderTexture; + opcodes[0x1b2]=funcSetShaderTexture; + opcodes[0x1b3]=funcGetTickCount; + opcodes[0x1b4]=SetStatMax; + opcodes[0x1b5]=SetStatMin; + opcodes[0x1b6]=SetCarTown; + opcodes[0x1b7]=fSetPCStatMax; + opcodes[0x1b8]=fSetPCStatMin; + opcodes[0x1b9]=fSetNPCStatMax; + opcodes[0x1ba]=fSetNPCStatMin; + opcodes[0x1bb]=fSetFakePerk; + opcodes[0x1bc]=fSetFakeTrait; + opcodes[0x1bd]=fSetSelectablePerk; + opcodes[0x1be]=fSetPerkboxTitle; + opcodes[0x1bf]=fIgnoreDefaultPerks; + opcodes[0x1c0]=fRestoreDefaultPerks; + opcodes[0x1c1]=fHasFakePerk; + opcodes[0x1c2]=fHasFakeTrait; + opcodes[0x1c3]=fAddPerkMode; + opcodes[0x1c4]=fClearSelectablePerks; + opcodes[0x1c5]=SetCritterHitChance; + opcodes[0x1c6]=SetBaseHitChance; + opcodes[0x1c7]=SetCritterSkillMod; + opcodes[0x1c8]=SetBaseSkillMod; + opcodes[0x1c9]=SetCritterPickpocket; + opcodes[0x1ca]=SetBasePickpocket; + opcodes[0x1cb]=SetPyromaniacMod; + opcodes[0x1cc]=fApplyHeaveHoFix; + opcodes[0x1cd]=SetSwiftLearnerMod; + opcodes[0x1ce]=SetLevelHPMod; + if(AllowUnsafeScripting) { + opcodes[0x1cf]=WriteByte; + opcodes[0x1d0]=WriteShort; + opcodes[0x1d1]=WriteInt; + for(int i=0x1d2;i<0x1dc;i++) + opcodes[i]=CallOffset; + } + opcodes[0x1dc]=ShowIfaceTag; + opcodes[0x1dd]=HideIfaceTag; + opcodes[0x1de]=IsIfaceTagActive; + opcodes[0x1df]=GetBodypartHitModifier; + opcodes[0x1e0]=SetBodypartHitModifier; + opcodes[0x1e1]=funcSetCriticalTable; + opcodes[0x1e2]=funcGetCriticalTable; + opcodes[0x1e3]=funcResetCriticalTable; + opcodes[0x1e4]=GetSfallArg; + opcodes[0x1e5]=SetSfallReturn; + opcodes[0x1e6]=SetApAcBonus; + opcodes[0x1e7]=GetApAcBonus; + opcodes[0x1e8]=SetApAcEBonus; + opcodes[0x1e9]=GetApAcEBonus; + opcodes[0x1ea]=InitHook; + opcodes[0x1eb]=GetIniString; + opcodes[0x1ec]=funcSqrt; + opcodes[0x1ed]=funcAbs; + opcodes[0x1ee]=funcSin; + opcodes[0x1ef]=funcCos; + opcodes[0x1f0]=funcTan; + opcodes[0x1f1]=funcATan; + opcodes[0x1f2]=SetPalette; + opcodes[0x1f3]=RemoveScript; + opcodes[0x1f4]=SetScript; + opcodes[0x1f5]=GetScript; + opcodes[0x1f6]=NBCreateChar; + opcodes[0x1f7]=fs_create; + opcodes[0x1f8]=fs_copy; + opcodes[0x1f9]=fs_find; + opcodes[0x1fa]=fs_write_byte; + opcodes[0x1fb]=fs_write_short; + opcodes[0x1fc]=fs_write_int; + opcodes[0x1fd]=fs_write_int; + opcodes[0x1fe]=fs_write_string; + opcodes[0x1ff]=fs_delete; + opcodes[0x200]=fs_size; + opcodes[0x201]=fs_pos; + opcodes[0x202]=fs_seek; + opcodes[0x203]=fs_resize; + opcodes[0x204]=get_proto_data; + opcodes[0x205]=set_proto_data; + opcodes[0x206]=set_self; + opcodes[0x207]=register_hook; + opcodes[0x208]=fs_write_bstring; + opcodes[0x209]=fs_read_byte; + opcodes[0x20a]=fs_read_short; + opcodes[0x20b]=fs_read_int; + opcodes[0x20c]=fs_read_float; + opcodes[0x20d]=list_begin; + opcodes[0x20e]=list_next; + opcodes[0x20f]=list_end; + opcodes[0x210]=sfall_ver_major; + opcodes[0x211]=sfall_ver_minor; + opcodes[0x212]=sfall_ver_build; + opcodes[0x213]=funcHeroSelectWin; + opcodes[0x214]=funcSetHeroRace; + opcodes[0x215]=funcSetHeroStyle; + opcodes[0x216]=set_critter_burst_disable; + opcodes[0x217]=get_weapon_ammo_pid; + opcodes[0x218]=set_weapon_ammo_pid; + opcodes[0x219]=get_weapon_ammo_count; + opcodes[0x21a]=set_weapon_ammo_count; + if(AllowUnsafeScripting) { + opcodes[0x21b]=WriteString; + } + opcodes[0x21c]=get_mouse_x; + opcodes[0x21d]=get_mouse_y; + opcodes[0x21e]=get_mouse_buttons; + opcodes[0x21f]=get_window_under_mouse; + opcodes[0x220]=get_screen_width; + opcodes[0x221]=get_screen_height; + opcodes[0x222]=stop_game; + opcodes[0x223]=resume_game; + opcodes[0x224]=create_message_window; + opcodes[0x225]=remove_trait; + opcodes[0x226]=get_light_level; + opcodes[0x227]=refresh_pc_art; + opcodes[0x228]=get_attack_type; + opcodes[0x229]=ForceEncounterWithFlags; + opcodes[0x22a]=set_map_time_multi; + opcodes[0x22b]=play_sfall_sound; + opcodes[0x22c]=stop_sfall_sound; + opcodes[0x22d]=create_array; + opcodes[0x22e]=set_array; + opcodes[0x22f]=get_array; + opcodes[0x230]=free_array; + opcodes[0x231]=len_array; + opcodes[0x232]=resize_array; + opcodes[0x233]=temp_array; + opcodes[0x234]=fix_array; + opcodes[0x235]=string_split; + opcodes[0x236]=list_as_array; + opcodes[0x237]=str_to_int; + opcodes[0x238]=str_to_flt; + opcodes[0x239]=scan_array; + opcodes[0x23a]=get_tile_pid; + opcodes[0x23b]=modified_ini; + opcodes[0x23c]=GetSfallArgs; + opcodes[0x23d]=SetSfallArg; + opcodes[0x23e]=force_aimed_shots; + opcodes[0x23f]=disable_aimed_shots; + opcodes[0x240]=mark_movie_played; + opcodes[0x241]=get_npc_level; + opcodes[0x242]=set_critter_skill_points; + opcodes[0x243]=get_critter_skill_points; + opcodes[0x244]=set_available_skill_points; + opcodes[0x245]=get_available_skill_points; + opcodes[0x246]=mod_skill_points_per_level; + opcodes[0x247]=set_perk_freq; + opcodes[0x248]=get_last_attacker; + opcodes[0x249]=get_last_target; + opcodes[0x24a]=block_combat; + opcodes[0x24b]=tile_under_cursor; + opcodes[0x24c]=gdialog_get_barter_mod; + opcodes[0x24d]=set_inven_ap_cost; + opcodes[0x24e]=op_substr; + opcodes[0x24f]=op_strlen; + opcodes[0x250]=op_sprintf; + opcodes[0x251]=op_ord; + // opcodes[0x252]= RESERVED + opcodes[0x253]=op_typeof; + opcodes[0x254]=op_save_array; + opcodes[0x255]=op_load_array; + opcodes[0x256]=op_get_array_key; + opcodes[0x257]=op_stack_array; + // opcodes[0x258]= RESERVED for arrays + // opcodes[0x259]= RESERVED for arrays + opcodes[0x25a]=op_reg_anim_destroy; + opcodes[0x25b]=op_reg_anim_animate_and_hide; + opcodes[0x25c]=op_reg_anim_combat_check; + opcodes[0x25d]=op_reg_anim_light; + opcodes[0x25e]=op_reg_anim_change_fid; + opcodes[0x25f]=op_reg_anim_take_out; + opcodes[0x260]=op_reg_anim_turn_towards; + opcodes[0x261]=op_explosions_metarule; + opcodes[0x262]=register_hook_proc; + opcodes[0x263]=funcPow; + opcodes[0x264]=funcLog; + opcodes[0x265]=funcExp; + opcodes[0x266]=funcCeil; + opcodes[0x267]=funcRound; + // opcodes[0x268]= RESERVED + // opcodes[0x269]= RESERVED + // opcodes[0x26a]=op_game_ui_redraw; + opcodes[0x26b]=op_message_str_game; + opcodes[0x26c]=op_sneak_success; + opcodes[0x26d]=op_tile_light; + opcodes[0x26e]=op_make_straight_path; + opcodes[0x26f]=op_obj_blocking_at; + opcodes[0x270]=op_tile_get_objects; + opcodes[0x271]=op_get_party_members; + opcodes[0x272]=op_make_path; + opcodes[0x273]=op_create_spatial; + opcodes[0x274]=op_art_exists; + opcodes[0x275]=op_obj_is_carrying_obj; +} + + +DWORD GetScriptProcByName(DWORD scriptPtr, const char* procName) { + DWORD result; + __asm { + mov edx, procName; + mov eax, scriptPtr; + call interpreFindProcedure_; + mov result, eax; + } + return result; +} +// loads script from .int file into a sScriptProgram struct, filling script pointer and proc lookup table +void LoadScriptProgram(sScriptProgram &prog, const char* fileName) { + DWORD scriptPtr; + __asm { + mov eax, fileName; + call loadProgram_; + mov scriptPtr, eax; + } + if (scriptPtr) { + const char** procTable = (const char **)_procTableStrs; + prog.ptr = scriptPtr; + // fill lookup table + for (int i=0; i<=SCRIPT_PROC_MAX; i++) { + prog.procLookup[i] = GetScriptProcByName(prog.ptr, procTable[i]); + } + prog.initialized = 0; + } else { + prog.ptr = NULL; + } +} + +void InitScriptProgram(sScriptProgram &prog) { + DWORD progPtr = prog.ptr; + if (prog.initialized == 0) { + __asm { + mov eax, progPtr; + call runProgram_; + mov edx, -1; + mov eax, progPtr; + call interpret_; + } + prog.initialized = 1; + } +} + +void AddProgramToMap(sScriptProgram &prog) { + sfallProgsMap[prog.ptr] = prog; +} +sScriptProgram* GetGlobalScriptProgram(DWORD scriptPtr) { + for (std::vector::iterator it = globalScripts.begin(); it != globalScripts.end(); it++) { + if (it->prog.ptr == scriptPtr) return &it->prog; + } + return NULL; +} + +// this runs after the game was loaded/started +void LoadGlobalScripts() { + isGameLoading = false; + HookScriptInit(); + dlogr("Loading global scripts", DL_SCRIPT); + WIN32_FIND_DATA file; + HANDLE h=FindFirstFileA("data\\scripts\\gl*.int", &file); + if (h != INVALID_HANDLE_VALUE) { + char* fName = file.cFileName; + // TODO: refactor script programs + sScriptProgram prog; + DWORD found = 1; + while (found) { + fName[strlen(fName) - 4] = 0; + dlogr(fName, DL_SCRIPT); + isGlobalScriptLoading = 1; + LoadScriptProgram(prog, fName); + if (prog.ptr) { + DWORD idx; + sGlobalScript gscript = sGlobalScript(prog); + idx = globalScripts.size(); + globalScripts.push_back(gscript); + AddProgramToMap(prog); + // initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array + InitScriptProgram(prog); + } + isGlobalScriptLoading = 0; + found = FindNextFileA(h, &file); + } + FindClose(h); + } + dlogr("Finished loading global scripts", DL_SCRIPT); + //ButtonsReload(); +} + +static DWORD _stdcall ScriptHasLoaded(DWORD script) { + for(DWORD d=0;dptr; + DWORD procNum = GetScriptProcByName(sptr, procName); + if (procNum != -1) { + RunScriptProcByNum(sptr, procNum); + } +} +void RunScriptProc(sScriptProgram* prog, DWORD procId) { + if (procId > 0 && procId <= SCRIPT_PROC_MAX) { + DWORD sptr = prog->ptr; + DWORD procNum = prog->procLookup[procId]; + if (procNum != -1) { + RunScriptProcByNum(sptr, procNum); + } + } +} +static void RunScript(sGlobalScript* script) { + script->count=0; + RunScriptProc(&script->prog, start_proc); // run "start" +} + +/** + Do some clearing after each frame: + - delete all temp arrays + - reset reg_anim_* combatstate checks +*/ +static void ResetStateAfterFrame() { + if (tempArrays.size()) { + for (std::set::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it) + FreeArray(*it); + tempArrays.clear(); + } + RegAnimCombatCheck(1); +} + +/** + Do some cleaning after each combat attack action +*/ +void AfterAttackCleanup() { + ResetExplosionSettings(); +} + +static void RunGlobalScripts1() { + if(idle>-1) Sleep(idle); + if(toggleHighlightsKey) { + //0x48C294 to toggle + if(KeyDown(toggleHighlightsKey)) { + if(!highlightingToggled) { + if(MotionSensorMode&4) { + DWORD scanner; + static const DWORD _inven_pid_is_carried_ptr=0x471CA0; + static const DWORD _item_m_dec_charges=0x4795A4; + __asm { + mov eax, ds:[0x6610B8]; + mov edx, 59; + call _inven_pid_is_carried_ptr; + mov scanner, eax; + } + if(scanner) { + if(MotionSensorMode&2) { + __asm { + mov eax, scanner; + call _item_m_dec_charges; //Returns -1 if the item has no charges + inc eax; + mov highlightingToggled, eax; + } + if(!highlightingToggled) DisplayConsoleMessage(HighlightFail2); + } else highlightingToggled=1; + } else { + DisplayConsoleMessage(HighlightFail1); + } + } else highlightingToggled=1; + if(highlightingToggled) { + __asm { + mov eax, dword ptr ds:[0x519578]; + xor ebx, ebx; + mov edx, 0x10; + mov ecx, 0x48C148; + call ecx; + } + } else { + highlightingToggled=2; + } + } + } else if(highlightingToggled) { + if(highlightingToggled==1) { + __asm { + mov eax, dword ptr ds:[0x519578]; + xor edx, edx; + mov ecx, 0x48C1E4; + call ecx; + } + } + highlightingToggled=0; + } + } + for(DWORD d=0;d=globalScripts[d].repeat) { + RunScript(&globalScripts[d]); + } + } + ResetStateAfterFrame(); +} +void RunGlobalScripts2() { + if(idle>-1) Sleep(idle); + for(DWORD d=0;d=globalScripts[d].repeat) { + RunScript(&globalScripts[d]); + } + } + ResetStateAfterFrame(); +} +void RunGlobalScripts3() { + if(idle>-1) Sleep(idle); + for(DWORD d=0;d=globalScripts[d].repeat) { + RunScript(&globalScripts[d]); + } + } + ResetStateAfterFrame(); +} + +void _stdcall HandleMapUpdateForScripts(DWORD procId) { + if (procId == map_enter_p_proc) { + // map changed, all game objects were destroyed and scripts detached, need to re-insert global scripts into the game + for (SfallProgsMap::iterator it = sfallProgsMap.begin(); it != sfallProgsMap.end(); it++) { + DWORD progPtr = it->second.ptr; + __asm { + mov eax, progPtr; + call runProgram_; + } + } + } + RunGlobalScriptsAtProc(procId); // gl* scripts of types 0 and 3 + RunHookScriptsAtProc(procId); // all hs_ scripts +} + +// run all global scripts of types 0 and 3 at specific procedure (if exist) +void _stdcall RunGlobalScriptsAtProc(DWORD procId) { + for (DWORD d = 0; d < globalScripts.size(); d++) { + if (globalScripts[d].mode != 0 && globalScripts[d].mode != 3) continue; + RunScriptProc(&globalScripts[d].prog, procId); + } +} + +void LoadGlobals(HANDLE h) { + DWORD count, unused; + ReadFile(h, &count, 4, &unused, 0); + if(unused!=4) return; + sGlobalVar var; + for(DWORD i=0;ifirst; + var.val=itr->second; + WriteFile(h, &var, sizeof(sGlobalVar), &unused, 0); + itr++; + } +} + +void ClearGlobals() { + globalVars.clear(); + for (array_itr it = arrays.begin(); it != arrays.end(); ++it) + it->second.clear(); + arrays.clear(); + savedArrays.clear(); +} +int GetNumGlobals() { return globalVars.size(); } +void GetGlobals(sGlobalVar* globals) { + glob_citr itr=globalVars.begin(); + int i=0; + while(itr!=globalVars.end()) { + globals[i].id=itr->first; + globals[i++].val=itr->second; + itr++; + } +} +void SetGlobals(sGlobalVar* globals) { + glob_itr itr=globalVars.begin(); + int i=0; + while(itr!=globalVars.end()) { + itr->second=globals[i++].val; + itr++; + } +} + +//fuctions to load and save appearance globals +void SetAppearanceGlobals(int race, int style) { + SetGlobalVar2("HAp_Race", race); + SetGlobalVar2("HApStyle", style); +} + +void GetAppearanceGlobals(int *race, int *style) { + *race=GetGlobalVar2("HAp_Race"); + *style=GetGlobalVar2("HApStyle"); +} + diff --git a/sfall/ScriptExtender.h b/sfall/ScriptExtender.h new file mode 100644 index 00000000..d1d030cf --- /dev/null +++ b/sfall/ScriptExtender.h @@ -0,0 +1,106 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "main.h" + +#define DATATYPE_NONE (0) +#define DATATYPE_INT (1) +#define DATATYPE_FLOAT (2) +#define DATATYPE_STR (3) + +struct sGlobalVar +{ + __int64 id; + int val; +}; + +#define SCRIPT_PROC_MAX (27) +typedef struct +{ + DWORD ptr; + DWORD procLookup[SCRIPT_PROC_MAX+1]; + char initialized; +} sScriptProgram; + +void ScriptExtenderSetup(); +void LoadGlobalScripts(); +void ClearGlobalScripts(); + +void RunGlobalScripts1(); +void RunGlobalScripts2(); +void RunGlobalScripts3(); +void _stdcall RunGlobalScriptsAtProc(DWORD procId); +void AfterAttackCleanup(); + +void LoadGlobals(HANDLE h); +void SaveGlobals(HANDLE h); +void ClearGlobals(); + +int GetNumGlobals(); +void GetGlobals(sGlobalVar* globals); +void SetGlobals(sGlobalVar* globals); + + +extern DWORD AddUnarmedStatToGetYear; +extern DWORD AvailableGlobalScriptTypes; + +void SetAppearanceGlobals(int race, int style); +void GetAppearanceGlobals(int *race, int *style); + +void _stdcall RegAnimCombatCheck(DWORD newValue); + +DWORD _stdcall ScriptHasLoaded(DWORD script); +// loads script from .int file into scripting engine, fill scriptPtr and proc table +void LoadScriptProgram(sScriptProgram &prog, const char* fileName); +// init program after load, needs to be called once +void InitScriptProgram(sScriptProgram &prog); +// execute script proc by internal proc number (from script's proc table, basically a sequential number of a procedure as defined in code, starting from 1) +void RunScriptProcByNum(DWORD sptr, DWORD procNum); +// execute script by specific proc name +void RunScriptProc(sScriptProgram* prog, const char* procName); +// execute script proc by procId from define.h +void RunScriptProc(sScriptProgram* prog, DWORD procId); + +void AddProgramToMap(sScriptProgram &prog); +sScriptProgram* GetGlobalScriptProgram(DWORD scriptPtr); + +char* _stdcall mysubstr(char* str, int pos, int length); +// for script functions - functions +static const DWORD GetArgType=0x4674F0; +static const DWORD GetArg=0x467500; +static const DWORD GetStringVar=0x4678E0; //eax = script ptr, edx = var type, ebx = var +static const DWORD CreateStringVar=0x467A80; //edx = ptr to string, eax = script +static const DWORD SetResult=0x4674DC; +static const DWORD SetResultType=0x46748C; +static const DWORD pushShortStack=0x46736C; // sometimes used instead of "SetResult" +// variables +static const DWORD combat_state = 0x510944; // DWORD global variable, use "test byte ptr ds:combat_state, 1" to check if combat is on +static char reg_anim_combat_check = 1; +extern DWORD isGlobalScriptLoading; + +// types for script variables +#define VAR_TYPE_INT (0xC001) +#define VAR_TYPE_FLOAT (0xA001) +#define VAR_TYPE_STR (0x9801) +#define VAR_TYPE_STR2 (0x9001) + +// script procs +#define start_proc (1) +#define map_enter_p_proc (15) +#define map_update_proc (23) \ No newline at end of file diff --git a/sfall/ScriptOps/AnimOps.hpp b/sfall/ScriptOps/AnimOps.hpp new file mode 100644 index 00000000..e701aab1 --- /dev/null +++ b/sfall/ScriptOps/AnimOps.hpp @@ -0,0 +1,200 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void _stdcall RegAnimCombatCheck(DWORD newValue) { + char oldValue = reg_anim_combat_check; + reg_anim_combat_check = (newValue > 0); + if (oldValue != reg_anim_combat_check) { + SafeWrite8(0x459C97, reg_anim_combat_check); // reg_anim_func + SafeWrite8(0x459D4B, reg_anim_combat_check); // reg_anim_animate + SafeWrite8(0x459E3B, reg_anim_combat_check); // reg_anim_animate_reverse + SafeWrite8(0x459EEB, reg_anim_combat_check); // reg_anim_obj_move_to_obj + SafeWrite8(0x459F9F, reg_anim_combat_check); // reg_anim_obj_run_to_obj + SafeWrite8(0x45A053, reg_anim_combat_check); // reg_anim_obj_move_to_tile + SafeWrite8(0x45A105, reg_anim_combat_check); // reg_anim_obj_run_to_tile + SafeWrite8(0x45AE53, reg_anim_combat_check); // reg_anim_animate_forever + } +} + +static void __declspec(naked) op_reg_anim_combat_check() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ecx, edx) // new value + _CHECK_ARG_INT(cx, end1) + __asm { + push edx + call RegAnimCombatCheck + } +end1: + _OP_END +} + +// new reg_anim functions (all using existing engine code) + +// checks if combat mode is enabled (using R8 8-bit register) and jumps to GOTOFAIL if it is (does nothing if reg_anim_combat_check is 0) +#define _CHECK_COMBAT_MODE(R8, GOTOFAIL) __asm { \ + __asm mov R8, reg_anim_combat_check \ + __asm test byte ptr ds:combat_state, R8 \ + __asm jnz GOTOFAIL } + +static void __declspec(naked) op_reg_anim_destroy() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, edx, esi) // object + _CHECK_ARG_INT(dx, end1) + _CHECK_COMBAT_MODE(al, end1) + __asm { + mov eax, esi // object + call register_object_must_erase_; + } +end1: + _OP_END +} + +static void __declspec(naked) op_reg_anim_animate_and_hide() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) // delay + _GET_ARG_R32(ebp, ecx, edi) // animID + _GET_ARG_R32(ebp, edx, eax) // object + // eax should not change until function call + _CHECK_ARG_INT(bx, end) + _CHECK_ARG_INT(cx, end) + _CHECK_ARG_INT(dx, end) + __asm { + _CHECK_COMBAT_MODE(bl, end) + mov ebx, esi // delay + mov edx, edi // animID + call register_object_animate_and_hide_; + } +end: + _OP_END +} + +static void __declspec(naked) op_reg_anim_light() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) // delay + _GET_ARG_R32(ebp, ecx, edi) // light radius + _GET_ARG_R32(ebp, edx, eax) // object + // eax should not change until function call + _CHECK_ARG_INT(bx, end) + _CHECK_ARG_INT(cx, end) + _CHECK_ARG_INT(dx, end) + __asm { + // check for valid radius + cmp di, 0 + jge dontfixMin + mov di, 0 + jmp dontfixMax +dontfixMin: + cmp di, 8 + jle dontfixMax + mov di, 8 +dontfixMax: + _CHECK_COMBAT_MODE(bl, end) + mov ebx, esi // delay + mov edx, edi // light radius + call register_object_light_; + } +end: + _OP_END +} + +static const DWORD tile_refresh_display_ = 0x4B12D8; // + +static void __declspec(naked) op_reg_anim_change_fid() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) // delay + _GET_ARG_R32(ebp, ecx, edi) // FID + _GET_ARG_R32(ebp, edx, eax) // object + // eax should not change until function call + _CHECK_ARG_INT(bx, end) + _CHECK_ARG_INT(cx, end) + _CHECK_ARG_INT(dx, end) + __asm { + _CHECK_COMBAT_MODE(bl, end) + mov ebx, esi // delay + mov edx, edi // FID + call register_object_change_fid_ + } +end: + _OP_END +} + +static void __declspec(naked) op_reg_anim_take_out() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) // delay - not used + _GET_ARG_R32(ebp, ecx, edi) // weapon hold frame ID + _GET_ARG_R32(ebp, edx, eax) // object + // eax should not change until function call + _CHECK_ARG_INT(bx, end) + _CHECK_ARG_INT(cx, end) + _CHECK_ARG_INT(dx, end) + __asm { + _CHECK_COMBAT_MODE(bl, end) + //mov ebx, esi // delay - not used + mov edx, edi // holdFrame + call register_object_take_out_; + } +end: + _OP_END +} + +static void __declspec(naked) op_reg_anim_turn_towards() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) // delay - not used + _GET_ARG_R32(ebp, ecx, edi) // tile + _GET_ARG_R32(ebp, edx, eax) // object + // eax should not change until function call + _CHECK_ARG_INT(bx, end) + _CHECK_ARG_INT(cx, end) + _CHECK_ARG_INT(dx, end) + __asm { + _CHECK_COMBAT_MODE(bl, end) + // mov ebx, esi // delay - not used + mov edx, edi // tile + call register_object_turn_towards_; + } +end: + _OP_END +} + + +static void __declspec(naked) op_explosions_metarule() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) // arg3 + _GET_ARG_R32(ebp, ecx, edi) // arg2 + _GET_ARG_R32(ebp, edx, eax) // arg1 + // eax should not change until function call + _CHECK_ARG_INT(bx, fail) + _CHECK_ARG_INT(cx, fail) + _CHECK_ARG_INT(dx, fail) + __asm { + push esi + push edi + push eax + call ExplosionsMetaruleFunc; + jmp end; + } +fail: + __asm { + mov eax, -1 + } +end: + _RET_VAL_INT(ebp) + _OP_END +} \ No newline at end of file diff --git a/sfall/ScriptOps/AsmMacros.h b/sfall/ScriptOps/AsmMacros.h new file mode 100644 index 00000000..80571309 --- /dev/null +++ b/sfall/ScriptOps/AsmMacros.h @@ -0,0 +1,184 @@ +#pragma once + +/* + MACROS for operators assembly code + + Notes: + - DO NOT add any comments within macros + - every macro should contain __asm {} block + - every assembly line should start with __asm and should NOT have semicolon in the end! + - use this macros outside of other __asm {} blocks (obviously) +*/ + +// rscript - 32-bit register where script pointer will be put (it is used for several related functions) +#define _OP_BEGIN(rscript) __asm \ +{ \ + __asm pushad \ + __asm mov rscript, eax \ +} + +#define _OP_END __asm \ +{ \ + __asm popad \ + __asm retn \ +} + +/* + Gets next argument from stack and puts its type to "rtype" and value to "rval" + arguments come in reverse order (from last to first) + all arguments should be valid r32 registers +*/ +#define _GET_ARG_R32(rscript, rtype, rval) __asm \ +{ \ + __asm mov eax, rscript \ + __asm call GetArgType \ + __asm mov rtype, eax \ + __asm mov eax, rscript \ + __asm call GetArg \ + __asm mov rval, eax \ +} + +/* + checks argument to be integer, and jumps to GOTOFAIL if it's not +*/ +#define _CHECK_ARG_INT(r16type, GOTOFAIL) __asm { \ + __asm cmp r16type, VAR_TYPE_INT \ + __asm jne GOTOFAIL } + +/* + checks argument to be float, and jumps to GOTOFAIL if it's not +*/ +#define _CHECK_ARG_FLOAT(r16type, GOTOFAIL) __asm { \ + __asm cmp r16type, VAR_TYPE_FLOAT \ + __asm jne GOTOFAIL } + + +/* + checks argument (which may be any type) if it's a string and retrieves it (overwrites value in rval) + num - any number, but it must be unique (used for label names) + r16type - 16bit register where value type is stored + rval - r32 where value is stored +*/ +#define _CHECK_PARSE_STR(num, rscript, r16type, rval) __asm { \ + __asm cmp r16type, VAR_TYPE_STR2 \ + __asm jz skipgetstr##num \ + __asm cmp r16type, VAR_TYPE_STR \ + __asm jnz notstring##num \ +__asm skipgetstr##num: \ + __asm mov edx, e##r16type \ + __asm mov ebx, rval \ + __asm mov eax, rscript \ + __asm call GetStringVar \ + __asm mov rval, eax } \ +notstring##num: + +#define _PARSE_STR_ARG(num, rscript, r16type, rval, GOTOFAIL) __asm { \ + __asm cmp r16type, VAR_TYPE_STR2 \ + __asm jz skipgetstr##num \ + __asm cmp r16type, VAR_TYPE_STR \ + __asm jnz GOTOFAIL \ + __asm skipgetstr##num: \ + __asm mov edx, e##r16type \ + __asm mov ebx, rval \ + __asm mov eax, rscript \ + __asm call GetStringVar \ + __asm mov rval, eax } + + +// must be immediately after C function call +#define _RET_VAL_INT(rscript) __asm { \ + __asm mov edx, eax \ + __asm mov eax, rscript \ + __asm call SetResult \ + __asm mov edx, VAR_TYPE_INT \ + __asm mov eax, rscript \ + __asm call SetResultType \ +} + +// return value stored in EAX as float +#define _RET_VAL_FLOAT(rscript) __asm { \ + __asm mov edx, eax \ + __asm mov eax, rscript \ + __asm call SetResult \ + __asm mov edx, VAR_TYPE_FLOAT \ + __asm mov eax, rscript \ + __asm call SetResultType \ +} + +/* + handle return value which may be of any type + num - any unique number + type - register or other expression (like memory address) where value type is stored (usually [esp]) +*/ +#define _RET_VAL_POSSIBLY_STR(num, rscript, type) __asm { \ + __asm mov ecx, eax \ + __asm mov edx, type \ + __asm cmp edx, VAR_TYPE_STR \ + __asm jne resultnotstr##num \ + __asm mov edx, eax \ + __asm mov eax, rscript \ + __asm call CreateStringVar \ + __asm mov ecx, eax \ +__asm resultnotstr##num: \ + __asm mov edx, ecx \ + __asm mov eax, rscript \ + __asm call SetResult \ + __asm mov edx, type \ + __asm mov eax, rscript \ + __asm call SetResultType \ +} + +/* + better way of handling new opcodes: + - no ASM code required + - all type checks should be done in the wrapped C-function + - use opArgs, opArgTypes to access arguments + - use opRet, opRetType to set return value + + argnum - number of arguments + func - C-function to call (should not have arguments) +*/ +#define _WRAP_OPCODE(argnum, func) __asm { \ + __asm pushad \ + __asm mov ebp, eax \ + __asm mov esi, argnum \ + __asm shl esi, 2 \ + __asm mov opArgCount, argnum \ + __asm mov opRetType, 0 \ +__asm loopbegin: \ + __asm test esi, esi \ + __asm jz loopend \ + __asm sub esi, 4 \ + __asm mov eax, ebp \ + __asm call GetArgType \ + __asm push eax \ + __asm call getSfallTypeByScriptType \ + __asm mov opArgTypes[esi], eax \ + __asm mov eax, ebp \ + __asm call GetArg \ + __asm mov opArgs[esi], eax \ + __asm jmp loopbegin \ +__asm loopend: \ + __asm call func \ + __asm mov eax, opRetType \ + __asm test eax, eax \ + __asm jz end \ + __asm push eax \ + __asm call getScriptTypeBySfallType \ + __asm mov ecx, eax \ + __asm mov edx, opRet \ + __asm cmp ecx, VAR_TYPE_STR \ + __asm jne notstring \ + __asm mov eax, ebp \ + __asm call CreateStringVar \ + __asm mov edx, eax \ +__asm notstring: \ + __asm mov eax, ebp \ + __asm call SetResult \ + __asm mov edx, ecx \ + __asm mov eax, ebp \ + __asm call SetResultType \ +__asm end: \ + __asm popad \ + __asm retn \ +} diff --git a/sfall/ScriptOps/FileSystemOps.hpp b/sfall/ScriptOps/FileSystemOps.hpp new file mode 100644 index 00000000..a3bc5e3b --- /dev/null +++ b/sfall/ScriptOps/FileSystemOps.hpp @@ -0,0 +1,531 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "FileSystem.h" +#include "ScriptExtender.h" + +//file system functions +static void __declspec(naked) fs_create() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + mov esi, eax; + mov eax, edi; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz fail; +next: + cmp bx, 0xc001; + jnz fail; + mov ebx, eax; + mov eax, edi; + call GetStringVar; + push esi; + push eax; + call FScreate; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, edi; + call SetResult; + mov eax, edi; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_copy() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0x9001; + jz next; + cmp di, 0x9801; + jnz fail; +next: + cmp si, 0x9001; + jz next2; + cmp si, 0x9801; + jnz fail; +next2: + mov ebx, eax; + mov edx, esi; + mov eax, ebp; + call GetStringVar; + mov esi, eax; + mov ebx, ecx; + mov edx, edi; + mov eax, ebp; + call GetStringVar; + push eax; + push esi; + call FScopy; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_find() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0x9001; + jz next; + cmp di, 0x9801; + jnz fail; +next: + mov ebx, eax; + mov edx, edi; + mov eax, ebp; + call GetStringVar; + push eax; + call FSfind; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_write_byte() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + push ecx; + push eax; + call FSwrite_byte; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_write_short() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + push ecx; + push eax; + call FSwrite_short; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_write_int() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jz next; + cmp si, 0xa001; + jnz end; +next: + push ecx; + push eax; + call FSwrite_int; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_write_string() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + mov esi, eax; + mov eax, edi; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + mov ebp, eax; + cmp bx, 0x9001; + jz next; + cmp bx, 0x9801; + jnz end; +next: + cmp dx, 0xc001; + jnz end; + mov edx, ebx; + mov ebx, esi; + mov eax, edi; + call GetStringVar; + push eax; + push ebp; + call FSwrite_string; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_write_bstring() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + mov esi, eax; + mov eax, edi; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + mov ebp, eax; + cmp bx, 0x9001; + jz next; + cmp bx, 0x9801; + jnz end; +next: + cmp dx, 0xc001; + jnz end; + mov edx, ebx; + mov ebx, esi; + mov eax, edi; + call GetStringVar; + push eax; + push ebp; + call FSwrite_bstring; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_read_byte() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call FSread_byte; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_read_short() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call FSread_short; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_read_int() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call FSread_int; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_read_float() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call FSread_int; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xa001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_delete() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end + push eax; + call FSdelete; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_size() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call FSsize; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_pos() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call FSpos; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fs_seek() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + push ecx; + push eax; + call FSseek; +end: + popad; + retn; + } +} +static void __declspec(naked) fs_resize() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + push ecx; + push eax; + call FSresize; +end: + popad; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/GraphicsOp.hpp b/sfall/ScriptOps/GraphicsOp.hpp new file mode 100644 index 00000000..5d00784d --- /dev/null +++ b/sfall/ScriptOps/GraphicsOp.hpp @@ -0,0 +1,555 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "Graphics.h" +#include "input.h" +#include "ScriptExtender.h" + +// graphics_functions +static void __declspec(naked) GraphicsFuncsAvailable() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + xor edx, edx; + mov ebx, GraphicsMode; + cmp ebx, 3; + jle end; + inc edx; +end: + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcLoadShader() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz error; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push eax; + call LoadShader; + mov edx, eax; + jmp result; +error: + mov edx, -1; +result: + mov eax, edi; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xC001; + mov eax, edi; + mov ebx, 0x46748C; + call ebx; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcFreeShader() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push eax; + call FreeShader; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcActivateShader() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push eax; + call ActivateShader; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcDeactivateShader() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push eax; + call DeactivateShader; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcGetShaderTexture() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + push eax + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov ebx, [esp]; + cmp bx, 0xC001; + jnz fail; + mov ebx, [esp+4]; + cmp bx, 0xc001; + jnz fail; + //set the new value + push ecx; + push edi; + push eax; + call GetShaderTexture; + mov edx, eax; + pop ecx; + jmp end; +fail: + mov edx, -1 +end: + //Pass back the result + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + //Restore registers and return + add esp, 8; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetShaderInt() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + push ebp; + sub esp, 0xc; + mov ecx, eax; + //Get args + mov ebx, 0x4674F0; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov [esp+8], eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov ebp, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov [esp], eax; + //Error check + cmp di, 0xC001; + jnz fail; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz fail; +next: + cmp si, 0xc001; + jnz fail; + mov eax, ecx; + mov ebx, ebp; + mov edi, 0x4678E0; + call edi; + mov [esp+4], eax; + call SetShaderInt; + jmp end; +fail: + add esp, 0xc; +end: + pop ebp; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetShaderTexture() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + push ebp; + sub esp, 0xc; + mov ecx, eax; + //Get args + mov ebx, 0x4674F0; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov [esp+8], eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov ebp, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov [esp], eax; + //Error check + cmp di, 0xC001; + jnz fail; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz fail; +next: + cmp si, 0xc001; + jnz fail; + mov eax, ecx; + mov ebx, ebp; + mov edi, 0x4678E0; + call edi; + mov [esp+4], eax; + call SetShaderTexture; + jmp end; +fail: + add esp, 0xc; +end: + pop ebp; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetShaderFloat() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + push ebp; + sub esp, 0xc; + mov ecx, eax; + //Get args + mov ebx, 0x4674F0; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov [esp+8], eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov ebp, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov [esp], eax; + //Error check + cmp di, 0xa001; + jz paramWasFloat; + cmp di, 0xc001; + jnz fail; + fild [esp+8]; + fstp [esp+8]; +paramWasFloat: + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz fail; +next: + cmp si, 0xc001; + jnz fail; + mov eax, ecx; + mov ebx, ebp; + mov edi, 0x4678E0; + call edi; + mov [esp+4], eax; + call SetShaderFloat; + jmp end; +fail: + add esp, 0xc; +end: + pop ebp; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetShaderVector() { + __asm { + push ebx; + push ecx; + push edx; + push ebp; + sub esp, 0x2a; + mov ebp, eax; + //Get args + mov ecx, 6; +argloopstart: + mov eax, ebp; + mov ebx, 0x4674F0; + call ebx; + mov word ptr [esp+ecx*2+0x16], ax; + mov eax, ebp; + mov ebx, 0x467500; + call ebx; + mov [esp+ecx*4-0x4], eax; + loop argloopstart; + //Error check + mov ecx, 4; +checkloopstart: + cmp word ptr [esp+ecx*2+0x1a], 0xa001; + jz paramWasFloat; + cmp word ptr [esp+ecx*2+0x1a], 0xc001; + jnz fail; + fild [esp+ecx*4+0x4]; + fstp [esp+ecx*4+0x4]; +paramWasFloat: + loop checkloopstart; + cmp word ptr [esp+0x1a], 0x9001; + jz next; + cmp word ptr [esp+0x1a], 0x9801; + jnz fail; +next: + cmp word ptr [esp+0x18], 0xc001; + jnz fail; + mov eax, ebp; + mov ebx, [esp+4]; + xor edx, edx; + mov dx, word ptr [esp+0x1a]; + mov edi, 0x4678E0; + call edi; + mov [esp+4], eax; + call SetShaderVector; + add esp, 0x12; + jmp end; +fail: + add esp, 0x2a; +end: + pop ebp; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcGetShaderVersion() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + call GetShaderVersion; + mov edx, eax; + mov eax, edi; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, edi; + mov ebx, 0x46748C; + call ebx; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetShaderMode() { + __asm { + push ebx; + push ecx; + push edx; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz fail; + cmp si, 0xC001; + jnz fail; + push eax; + call SetShaderMode; + jmp end +fail: + pop eax; +end: + pop esi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcForceGraphicsRefresh() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push eax; + call ForceGraphicsRefresh; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/InterfaceOp.hpp b/sfall/ScriptOps/InterfaceOp.hpp new file mode 100644 index 00000000..f1983b1e --- /dev/null +++ b/sfall/ScriptOps/InterfaceOp.hpp @@ -0,0 +1,460 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "input.h" +#include "ScriptExtender.h" + + +// input_functions +static void __declspec(naked) InputFuncsAvailable() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov edx, 1; //They're always available from 2.9 on + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) KeyPressed() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz fail; + push ecx; + push eax; + call KeyDown; + mov edx, eax; + pop ecx; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcTapKey() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + test eax, eax; + jl end; + cmp eax, 255; + jge end; + push eax; + call TapKey; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +//// *** From helios *** //// +static void __declspec(naked) get_mouse_x() { + __asm { + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x6AC7A8]; + add edx, ds:[0x6AC7D0]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} + +//Return mouse y position +static void __declspec(naked) get_mouse_y() { + __asm { + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x6AC7A4]; + add edx, ds:[0x6AC7CC]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} + +//Return pressed mouse button (1=left, 2=right, 3=left+right) +static void __declspec(naked) get_mouse_buttons() { + __asm { + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x0051E2AC]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} + +//Return the window number under the mous +static void __declspec(naked) get_window_under_mouse() { + __asm { + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x0051E404]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} +//Return screen width +static void __declspec(naked) get_screen_width() { + __asm { + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x4CAD6B]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} +//Return screen height +static void __declspec(naked) get_screen_height() { + __asm { + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x4CAD66]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} +//Stop game, the same effect as open charsscreen or inventory +static void __declspec(naked) stop_game() { + __asm { + push ebx; + mov ebx, 0x00482104; + call ebx; + pop ebx; + retn; + } +} + +//Resume the game when it is stopped +static void __declspec(naked) resume_game() { + __asm { + push ebx; + mov ebx, 0x004820C0; + call ebx; + pop ebx; + retn; + } +} + +//Create a message window with given string +static const DWORD createWindow=0x41CF20; +static void __declspec(naked) create_message_window() { + __asm { + pushad + mov ebx, dword ptr ds:[0x51E3B0]; + cmp ebx, 0x65; + je end; + + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, ecx; + call GetStringVar; + mov esi, eax + + mov ecx, eax; + mov eax, 3; + push 1; + mov al, ds:0x006AB718; + push eax; + push 0; + push eax; + push 0x74; + mov ecx, 0xC0; + mov eax, esi; + xor ebx, ebx; + xor edx, edx; + call createWindow; + //xor eax, eax; +end: + popad + ret; + } +} + +static void __declspec(naked) GetViewportX() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x51DE2C]; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetViewportY() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x51DE30]; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetViewportX() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + mov ds:[0x51DE2C], eax +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetViewportY() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + mov ds:[0x51DE30], eax +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +static const DWORD _refresh_box_bar_win=0x4614CC; +static const DWORD _pc_flag_on=0x42E26C; +static const DWORD _pc_flag_off=0x42E220; +static void __declspec(naked) ShowIfaceTag() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp eax, 3; + je falloutfunc; + cmp eax, 4; + je falloutfunc; + push eax; + call AddBox; + call _refresh_box_bar_win; + jmp end; +falloutfunc: + call _pc_flag_on; +end: + popad; + retn; + } +} +static void __declspec(naked) HideIfaceTag() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp eax, 3; + je falloutfunc; + cmp eax, 4; + je falloutfunc; + push eax; + call RemoveBox; + call _refresh_box_bar_win; + jmp end; +falloutfunc: + call _pc_flag_off; +end: + popad; + retn; + } +} +static void __declspec(naked) IsIfaceTagActive() { + __asm { + pushad; + sub esp, 4; + mov ebx, eax; + call GetArgType; + mov edx, eax; + mov eax, ebx; + call GetArg; + cmp dx, 0xC001; + jnz fail; + cmp eax, 3; + je falloutfunc; + cmp eax, 4; + je falloutfunc; + push eax; + call GetBox; + mov edx, eax; + jmp end; +falloutfunc: + mov ecx, eax; + mov eax, dword ptr ds:[0x6610B8]; + mov edx, esp; + mov eax, [eax+0x64]; + mov edi, 0x4A2108; + call edi; + mov edx, 1; + shl edx, cl; + mov ecx, [esp]; + mov eax, [ecx+0x20]; + and eax, edx; + jz fail; + xor edx, edx; + inc edx; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ebx; + call SetResult; + mov eax, ebx; + mov edx, 0xc001; + call SetResultType; + add esp, 4; + popad; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/MemoryOp.hpp b/sfall/ScriptOps/MemoryOp.hpp new file mode 100644 index 00000000..1c6776e4 --- /dev/null +++ b/sfall/ScriptOps/MemoryOp.hpp @@ -0,0 +1,337 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "ScriptExtender.h" + +// memory_reading_funcs +static void __declspec(naked) ReadByte() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz error; + movzx edx, byte ptr ds:[eax]; + jmp result; +error: + mov edx, 0; +result: + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ReadShort() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz error; + movzx edx, word ptr ds:[eax]; + jmp result; +error: + mov edx, 0; +result: + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ReadInt() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz error; + mov edx, dword ptr ds:[eax]; + jmp result; +error: + mov edx, 0; +result: + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ReadString() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz error; + mov edx, eax; + jmp result; +error: + mov edx, 0; +result: + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0x9801; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) WriteByte() { + __asm { + pushad + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edx, eax; + mov eax, ecx; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + cmp di, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + //mov byte ptr ds:[eax], dl; + and edx, 0xff; + push edx; + push eax; + call SafeWrite8; +end: + popad; + retn; + } +} +static void __declspec(naked) WriteShort() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edx, eax; + mov eax, ecx; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + cmp di, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + //mov word ptr ds:[eax], dx; + and edx, 0xffff; + push edx; + push eax; + call SafeWrite16; +end: + popad; + retn; + } +} +static void __declspec(naked) WriteInt() { + __asm { + pushad + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edx, eax; + mov eax, ecx; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + cmp di, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + //mov dword ptr ds:[eax], edx; + push edx; + push eax; + call SafeWrite32; +end: + popad + retn; + } +} +static void _stdcall WriteStringInternal(const char* str, char* addr) { + bool hitnull=false; + while(*str) { + if(!*addr) hitnull=true; + if(hitnull&&addr[1]) break; + *addr=*str; + addr++; + str++; + } + *addr=0; +} +static void __declspec(naked) WriteString() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edi, eax; + mov eax, ecx; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp si, 0x9001; + jz next; + cmp si, 0x9801; + jnz end; +next: + mov ebx, edi; + mov edx, esi; + mov esi, eax; + mov eax, ecx; + call GetStringVar; + push esi; + push eax; + call WriteStringInternal; + jmp end; +end: + popad; + retn; + } +} +static void _stdcall CallOffsetInternal(DWORD func, DWORD script) { + func=(func>>2) - 0x1d2; + bool ret=func>=5; + int argcount=func%5; + DWORD args[5]; + DWORD illegalarg=0; + for(int i=argcount*4;i>=0;i-=4) { + __asm { + mov eax, script; + call GetArgType; + cmp ax, 0xc001; + jz legal; + inc illegalarg; +legal: + mov eax, script; + call GetArg; + lea ecx, args; + add ecx, i; + mov [ecx], eax; + } + } + + if(illegalarg) { + args[0]=0; + } else { + __asm { + mov eax, args[1]; + mov edx, args[2]; + mov ebx, args[3]; + mov ecx, args[4]; + mov edi, args[0]; + call edi; + mov args[0], eax; + } + } + if(ret) { + __asm { + mov eax, script; + mov edx, args[0]; + call SetResult; + mov eax, script; + mov edx, 0xc001; + call SetResultType; + } + } +} +static void __declspec(naked) CallOffset() { + __asm { + pushad; + push eax; + push edx; + call CallOffsetInternal; + popad; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/MiscOps.hpp b/sfall/ScriptOps/MiscOps.hpp new file mode 100644 index 00000000..a3181a7e --- /dev/null +++ b/sfall/ScriptOps/MiscOps.hpp @@ -0,0 +1,1743 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "KillCounter.h" +#include "Knockback.h" +#include "movies.h" +#include "HeroAppearance.h" +#include "AI.h" + +#include "ScriptExtender.h" + +/* + * Misc operators + */ + +static DWORD dmModelNamePtr=(DWORD)dmModelName; +static DWORD dfModelNamePtr=(DWORD)dfModelName; +static DWORD MovieNamesPtr=(DWORD)MoviePaths; + + +//// *** End Helios *** /// +static void _stdcall strcpy_p(char* to, const char* from) { + strcpy_s(to, 64, from); +} +static void __declspec(naked) SetDMModel() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push eax; + push dmModelNamePtr; + call strcpy_p; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetDFModel() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push eax; + push dfModelNamePtr; + call strcpy_p; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetMoviePath() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov edi, eax; + mov ecx, 0x4674F0; + call ecx; + mov ebx, eax; + mov eax, edi; + mov ecx, 0x467500; + call ecx; + mov esi, eax; + mov eax, edi; + mov ecx, 0x4674F0; + call ecx; + mov edx, eax; + mov eax, edi; + mov ecx, 0x467500; + call ecx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + cmp bx, 0xc001; + jnz end; + cmp esi, 0; + jl end; + cmp esi, MaxMovies; + jge end; + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push eax; + mov eax, esi; + mov esi, 65; + mul si; + add eax, MovieNamesPtr; + push eax; + call strcpy_p; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetYear() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + sub esp, 4; + xor eax, eax; + xor edx, edx; + mov ebx, esp; + mov ecx, 0x004A3338; + call ecx; + mov edx, [esp]; + mov eax, AddUnarmedStatToGetYear; + test eax, eax; + jz end; + add edx, ds:[0x51C3BC]; +end: + mov eax, edi; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, edi; + mov ebx, 0x46748C; + call ebx; + add esp, 4; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GameLoaded() { + __asm { + push ebx; + push ecx; + push edx; + push eax; + push eax; + call ScriptHasLoaded; + mov edx, eax; + pop eax; + mov ecx, eax; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +void _stdcall SetMapMulti(float d); +static void __declspec(naked) set_map_time_multi() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xa001; + jz paramWasFloat; + cmp dx, 0xc001; + jnz fail; + push eax; + fild dword ptr [esp]; + fstp dword ptr [esp]; + jmp end; +paramWasFloat: + push eax; +end: + call SetMapMulti; +fail: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetPipBoyAvailable() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp eax, 0; + jl end; + cmp eax, 1; + jg end; + mov byte ptr ds:[0x00596C7B], al; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + + + +// Kill counters +static void __declspec(naked) GetKillCounter() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz fail; + cmp eax, 19; + jge fail; + mov edx, ds:[0x0056D780+eax*4]; + jmp end; +fail: + + xor edx, edx; +end: + mov eax, ecx + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ModKillCounter() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + cmp eax, 19; + jge end; + add ds:[0x0056D780+eax*4], edi; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +//Knockback +static void __declspec(naked) SetKnockback() { + __asm { + sub esp, 0xc; + mov ecx, eax; + //Get args + mov ebx, 0x4674F0; + call ebx; //First arg type + mov edi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; //First arg + mov [esp+8], eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; //Second arg type + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; //Second arg + mov [esp+4], eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; //Third arg type + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; //Third arg + mov [esp], eax; + //Error check + cmp di, 0xa001; + jz paramWasFloat; + cmp di, 0xc001; + jnz fail; + fild [esp+8]; + fstp [esp+8]; +paramWasFloat: + cmp dx, 0xc001; + jnz fail; + cmp si, 0xc001; + jnz fail; + call KnockbackSetMod; + jmp end; +fail: + add esp, 0x10; +end: + popad; + retn; + } +} +static void __declspec(naked) SetWeaponKnockback() { + __asm { + pushad; + push 0; + jmp SetKnockback; + } +} +static void __declspec(naked) SetTargetKnockback() { + __asm { + pushad; + push 1; + jmp SetKnockback; + } +} +static void __declspec(naked) SetAttackerKnockback() { + __asm { + pushad; + push 2; + jmp SetKnockback; + } +} +static void __declspec(naked) RemoveKnockback() { + __asm { + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz fail; + push eax; + call KnockbackRemoveMod; + jmp end; +fail: + add esp, 4; +end: + popad; + retn; + } +} +static void __declspec(naked) RemoveWeaponKnockback() { + __asm { + pushad; + push 0; + jmp RemoveKnockback; + } +} +static void __declspec(naked) RemoveTargetKnockback() { + __asm { + pushad; + push 1; + jmp RemoveKnockback; + } +} +static void __declspec(naked) RemoveAttackerKnockback() { + __asm { + pushad; + push 2; + jmp RemoveKnockback; + } +} +static void __declspec(naked) GetKillCounter2() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz fail; + cmp eax, 38; + jge fail; + movzx edx, word ptr ds:[0x0056D780+eax*2]; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ecx + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ModKillCounter2() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + cmp eax, 38; + jge end; + add word ptr ds:[0x0056D780+eax*2], di; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +static void __declspec(naked) GetActiveHand() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov edx, dword ptr ds:[0x518F78]; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ToggleActiveHand() { + __asm { + push ebx; + mov eax, 1; + mov ebx, 0x0045F404; + call ebx; + pop ebx; + retn; + } +} + +static void __declspec(naked) EaxAvailable() { + __asm { + push ebx; + push eax; + push edx; + push edi; + mov edi, eax; + xor edx, edx + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, edi; + mov ebx, 0x46748C; + call ebx; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static char* NPCToInc; +static void _stdcall IncNPCLevel4(char* npc) { + if(_stricmp(npc, NPCToInc)) { + SafeWrite8(0x00495C50, 0xe9); + SafeWrite32(0x00495C51, 0x000001fc); + } else { + SafeWrite16(0x00495C50, 0x840f); //Want to keep this check intact. + SafeWrite32(0x00495C52, 0x000001fb); + + //SafeWrite16(0x00495C50, 0x9090); + //SafeWrite32(0x00495C52, 0x90909090); + SafeWrite16(0x00495C77, 0x9090); //Check that the player is high enough for the npc to consider this level + SafeWrite32(0x00495C79, 0x90909090); + //SafeWrite16(0x00495C8C, 0x9090); //Check that the npc isn't already at its maximum level + //SafeWrite32(0x00495C8E, 0x90909090); + SafeWrite16(0x00495CEC, 0x9090); //Check that the npc hasn't already levelled up recently + SafeWrite32(0x00495CEE, 0x90909090); + if(!npcautolevel) { + SafeWrite16(0x00495D22, 0x9090);//Random element + SafeWrite32(0x00495D24, 0x90909090); + } + } +} +static void __declspec(naked) IncNPCLevel3() { + __asm { + pushad; + push eax; + call IncNPCLevel4; + popad; + mov eax, 0x00495BF9; + jmp eax; + } +} +static void _stdcall IncNPCLevel2(char* npc) { + NPCToInc=npc; + SafeWrite8(0x00495BEB, 0xe9); //Replace the debug output with a jmp + SafeWrite32(0x00495BEC, ((DWORD)&IncNPCLevel3) - 0x00495BF0); + __asm { + mov eax, 0x00495B60; + call eax; + } + SafeWrite16(0x00495C50, 0x840f); + SafeWrite32(0x00495C52, 0x000001fb); + SafeWrite16(0x00495C77, 0x8c0f); + SafeWrite32(0x00495C79, 0x000001d4); + //SafeWrite16(0x00495C8C, 0x8d0f); + //SafeWrite32(0x00495C8E, 0x000001bf); + SafeWrite16(0x00495CEC, 0x850f); + SafeWrite32(0x00495CEE, 0x00000130); + if(!npcautolevel) { + SafeWrite16(0x00495D22, 0x8f0f); + SafeWrite32(0x00495D24, 0x00000129); + } +} +static void __declspec(naked) IncNPCLevel() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, GetStringVar; + call ecx; + push eax; + call IncNPCLevel2; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static int _stdcall get_npc_level2(DWORD numMembers, DWORD maxMembers, DWORD* members, DWORD* pids, DWORD* words, const char* name) { + static const DWORD _critter_name=0x42D0A8; + for(DWORD i=0;i=64) return -1; + key=strstr(key+1, "|"); + if(!key) return -1; + DWORD seclen=(DWORD)key - ((DWORD)c + filelen + 1); + if(seclen>32) return -1; + + char file[67]; + file[0]='.'; + file[1]='\\'; + memcpy(&file[2], c, filelen); + file[filelen+2]=0; + + char section[33]; + memcpy(section, &c[filelen+1], seclen); + section[seclen]=0; + + key++; + if(string) { + IniStrBuffer[0]=0; + GetPrivateProfileStringA(section, key, "", IniStrBuffer, 128, file); + return (DWORD)&IniStrBuffer[0]; + } else { + return GetPrivateProfileIntA(section, key, -1, file); + } +} +static void __declspec(naked) GetIniSetting() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz error; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push 0; + push eax; + call GetIniSetting2; + mov edx, eax; + jmp result; +error: + mov edx, -1; +result: + mov eax, edi; + call SetResult; + mov edx, 0xC001; + mov eax, edi; + call SetResultType; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetIniString() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz error; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, 0x4678E0; + call ecx; + push 1; + push eax; + call GetIniSetting2; + mov edx, eax; + mov eax, edi; + call CreateStringVar; + mov edx, eax; + mov ebx, 0x9801; + jmp result; +error: + xor edx, edx; + mov ebx, 0xc001 +result: + mov eax, edi; + call SetResult; + mov edx, ebx; + mov eax, edi; + call SetResultType; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static DWORD _stdcall GetTickCount2() { + return GetTickCount(); +} +static void __declspec(naked) funcGetTickCount() { + __asm { + pushad; + mov edi, eax; + call GetTickCount2; + mov edx, eax; + mov eax, edi; + call SetResult; + mov edx, 0xc001; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) SetCarTown() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + mov ds:[0x00672E68], eax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +static void __declspec(naked) SetLevelHPMod() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + push 0x4AFBC1; + call SafeWrite8; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + + +static void __declspec(naked) GetBodypartHitModifier() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz fail; + cmp eax, 9; + jg fail; + test eax, eax; + jl fail; + mov edx, ds:[0x510954+eax*4]; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) SetBodypartHitModifier() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + mov ebx, eax; + mov eax, ecx; + call GetArgType; + xchg eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp cx, 0xC001; + jnz end; + cmp eax, 9; + jg end; + test eax, eax; + jl end; + mov ds:[0x510954+eax*4], ebx; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetCriticalTable() { + __asm { + pushad; + mov ecx, eax; + xor ebx, ebx; + mov edx, 5; +loops: + mov eax, ecx; + call GetArgType; + cmp ax, 0xc001; + jz skip1; + inc ebx; +skip1: + mov eax, ecx; + call GetArg; + push eax; + dec dx; + jnz loops; + test ebx, ebx; + jnz fail; + call SetCriticalTable; + jmp end; +fail: + add esp, 20; +end: + popad; + retn; + } +} +static void __declspec(naked) funcGetCriticalTable() { + __asm { + pushad; + mov edi, eax; + xor ebx, ebx; + mov dl, 4; +loops: + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + jz skip1; + inc ebx; +skip1: + mov eax, edi; + call GetArg; + push eax; + dec dx; + jnz loops; + test ebx, ebx; + jz fail; + call ResetCriticalTable; + mov edx, eax; + jmp end; +fail: + add esp, 16; + xor edx, edx; +end: + mov eax, edi; + call SetResult; + mov eax, edi; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) funcResetCriticalTable() { + __asm { + pushad; + mov ecx, eax; + xor ebx, ebx; + mov dl, 4; +loops: + mov eax, ecx; + call GetArgType; + cmp ax, 0xc001; + jz skip1; + inc ebx; +skip1: + mov eax, ecx; + call GetArg; + push eax; + dec dx; + jnz loops; + test ebx, ebx; + jz fail; + call ResetCriticalTable; + jmp end; +fail: + add esp, 16; +end: + popad; + retn; + } +} +static void __declspec(naked) SetApAcBonus() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xc001; + jnz end; + mov StandardApAcBonus, ax; +end: + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) GetApAcBonus() { + __asm { + push ecx; + push edx; + mov ecx, eax; + movzx edx, StandardApAcBonus; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) SetApAcEBonus() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xc001; + jnz end; + mov ExtraApAcBonus, ax; +end: + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) GetApAcEBonus() { + __asm { + push ecx; + push edx; + mov ecx, eax; + movzx edx, ExtraApAcBonus; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType + pop edx; + pop ecx; + retn; + } +} + +static const DWORD CopyPalette=0x493B48; +static const DWORD LoadPalette=0x4C78E4; +static void __declspec(naked) SetPalette() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + //mov eax, 0x663FD0; + //call CopyPalette; + mov eax, ecx; + call GetStringVar; + call LoadPalette; + mov eax, 0x51DF34; + call CopyPalette; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +//numbers subgame functions +static void __declspec(naked) NBCreateChar() { + __asm { + /*pushad; + push eax; + call NumbersCreateChar; + mov edx, eax; + pop eax; + mov ecx, eax; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + popad;*/ + retn; + } +} + +static const DWORD proto_ptr=0x4A2108; +static void __declspec(naked) get_proto_data() { + __asm { + pushad; + sub esp, 4; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + cmp si, 0xc001; + jnz fail; + mov edx, esp; + call proto_ptr; + mov eax, [esp]; + test eax, eax; + jz fail; + mov edx, [eax+ecx/**4*/]; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + add esp, 4; + popad; + retn; + } +} +static void __declspec(naked) set_proto_data() { + __asm { + pushad; + sub esp, 4; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + mov ebx, eax; + mov eax, ebp; + call GetArgType; + xchg eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + cmp bp, 0xc001; + jnz end; + //mov eax, [eax+0x64]; + mov edx, esp; + call proto_ptr; + mov eax, [esp]; + test eax, eax; + jz end; + mov [eax+ebx/**4*/], ecx; +end: + add esp, 4; + popad; + retn; + } +} + +static void __declspec(naked) funcHeroSelectWin() {//for opening the appearance selection window + __asm { + push ebx; + push ecx; + push edx; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + push eax; + cmp dx, 0xC001; + jnz fail; + call HeroSelectWindow; + jmp end; +fail: + pop eax; +end: + pop esi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetHeroStyle() {//for setting the hero style/appearance takes an 1 int + __asm { + push ebx; + push ecx; + push edx; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + push eax; + cmp dx, 0xC001; + jnz fail; + call SetHeroStyle; + jmp end; +fail: + pop eax; +end: + pop esi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) funcSetHeroRace() {// for setting the hero race takes an 1 int + __asm { + push ebx; + push ecx; + push edx; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + push eax; + cmp dx, 0xC001; + jnz fail; + call SetHeroRace; + jmp end; +fail: + pop eax; +end: + pop esi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +static void __declspec(naked) get_light_level() { + __asm { + pushad; + mov ecx, eax; + mov edx, ds:[0x51923C]; + call SetResult + mov edx, 0xc001; + mov eax, ecx; + call SetResultType + popad; + retn; + } +} +static void __declspec(naked) refresh_pc_art() { + __asm { + push ebx; + push ecx; + push edx; + call RefreshPCArt; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) get_attack_type() { + __asm { + push edx; + push ecx; + mov ecx, eax; + mov edx, ds:[0x56d2b4]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop ecx; + pop edx; + retn; + } +} +static void __declspec(naked) play_sfall_sound() { + __asm { + pushad + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + mov esi, eax; + mov eax, edi; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + cmp bx, 0xc001; + jnz end; + mov ebx, eax; + mov eax, edi; + call GetStringVar; + push esi; + push eax; + mov eax, esi; + mov esi, 65; + call PlaySfallSound; + mov edx, eax; + mov eax, edi; + call SetResult; + mov eax, edi; + mov edx, 0xc001; + call SetResultType; +end: + popad + retn; + } +} +static void __declspec(naked) stop_sfall_sound() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + push eax; + call StopSfallSound; +end: + popad; + retn; + } +} + +static const DWORD _tile_coord=0x4B1674; +static const DWORD _square_num=0x4B1F04; +static void __declspec(naked) get_tile_pid() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + sub esp, 8; + lea edx, [esp]; + lea ebx, [esp+4]; + call _tile_coord; + mov eax, [esp]; + mov edx, [esp+4]; + call _square_num; + mov edx, ds:[0x631E40]; + movzx edx, word ptr ds:[edx+eax*4]; + add esp, 8; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static DWORD modifiedIni; +static void __declspec(naked) modified_ini() { + __asm { + pushad; + mov edx, modifiedIni; + mov ebp, eax; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) force_aimed_shots() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + call ForceAimedShots; +end: + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) disable_aimed_shots() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + call DisableAimedShots; +end: + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) mark_movie_played() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + test eax, eax; + jl end; + cmp eax, 0x11; + jge end; + mov byte ptr ds:[eax+0x596C78], 1; +end: + pop edx; + pop ecx; + retn; + } +} +static void __declspec(naked) get_last_attacker() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call AIGetLastAttacker; + mov edx, eax; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) get_last_target() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call AIGetLastTarget; + mov edx, eax; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) block_combat() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + call AIBlockCombat; +end: + popad + retn; + } +} +static const DWORD _mouse_get_position=0x4CA9DC; +static const DWORD _tile_num=0x4B1754; +static void __declspec(naked) tile_under_cursor() { + __asm { + push edx; + push ecx; + push ebx; + mov ecx, eax; + sub esp, 8; + lea edx, [esp]; + lea eax, [esp+4]; + call _mouse_get_position; + mov ebx, dword ptr ds:[0x519578]; + mov edx, [esp]; + mov eax, [esp+4]; + call _tile_num; + mov edx, eax; + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + add esp, 8; + pop ebx; + pop ecx; + pop edx; + retn; + } +} +static void __declspec(naked) gdialog_get_barter_mod() { + __asm { + push edx; + push ecx; + mov ecx, eax; + mov edx, dword ptr ds:[0x51873C]; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + pop ecx; + pop edx; + retn; + } +} +static void __declspec(naked) set_inven_ap_cost() { + __asm { + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + call SetInvenApCost; +end: + pop edx; + pop ecx; + retn; + } +} + +static const DWORD sneak_working = 0x56D77C; // DWORD var +static void __declspec(naked) op_sneak_success() { + _OP_BEGIN(ebp) + __asm { + mov eax, sneak_working + mov eax, [eax] + } + _RET_VAL_INT(ebp) + _OP_END +} + + +static const DWORD light_get_tile_ = 0x47A980; // aElev, aTilenum +static void __declspec(naked) op_tile_light() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, edi) // arg2 - tile + _GET_ARG_R32(ebp, ecx, esi) // arg1 - elevation + _CHECK_ARG_INT(bx, fail) + __asm { + mov eax, esi + mov edx, edi + call light_get_tile_ + jmp end +fail: + mov eax, 0 +end: + } + _RET_VAL_INT(ebp) + _OP_END +} diff --git a/sfall/ScriptOps/ObjectsOps.hpp b/sfall/ScriptOps/ObjectsOps.hpp new file mode 100644 index 00000000..ca71ea9a --- /dev/null +++ b/sfall/ScriptOps/ObjectsOps.hpp @@ -0,0 +1,564 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "Inventory.h" +#include "ScriptExtender.h" + +//script control functions + +static const DWORD scr_remove=0x4A61D4; +static void __declspec(naked) RemoveScript() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xc001; + jnz end; + test eax, eax; + jz end; + mov edx, eax; + mov eax, [eax+0x78]; + cmp eax, 0xffffffff; + jz end; + call scr_remove; + mov dword ptr [edx+0x78], 0xffffffff; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +static const DWORD obj_new_sid_inst=0x49AAC0; +static const DWORD exec_script_proc=0x4A4810; +//static const DWORD obj_new_sid_inst=0x49A9B4; +static void __declspec(naked) SetScript() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + mov ebx, eax; + mov eax, ecx; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xc001; + jnz end; + cmp di, 0xc001; + jnz end; + test eax, eax; + jz end; + mov esi, [eax+0x78]; + cmp esi, 0xffffffff; + jz newscript + push eax; + mov eax, esi; + call scr_remove; + pop eax; + mov dword ptr [eax+0x78], 0xffffffff; +newscript: + mov esi, 1; + test ebx, 0x80000000; + jz execMapEnter; + xor esi, esi; + xor ebx, 0x80000000; +execMapEnter: + mov ecx, eax; + mov edx, 3; // script_type_item + mov edi, [eax+0x64]; + shr edi, 24; + cmp edi, 1; + jnz notCritter; + inc edx; // 4 - "critter" type script +notCritter: + dec ebx; + call obj_new_sid_inst; + mov eax, [ecx+0x78]; + mov edx, 1; // start + call exec_script_proc; + cmp esi, 1; // run map enter? + jnz end; + mov eax, [ecx+0x78]; + mov edx, 0xf; // map_enter_p_proc + call exec_script_proc; +end: + popad; + retn; + } +} + +static const DWORD scr_new_=0x4A5F28; // eax - script index from scripts lst, edx - type (0 - system, 1 - spatials, 2 - time, 3 - items, 4 - critters) +static const DWORD scr_ptr_=0x4A5E34; // eax - scriptId, edx - **TScript (where to store script pointer) + +static void _stdcall op_create_spatial2() { + DWORD scriptIndex = GetOpArgInt(0), + tile = GetOpArgInt(1), + elevation = GetOpArgInt(2), + radius = GetOpArgInt(3), + scriptId, tmp, objectPtr, + scriptPtr; + __asm { + lea eax, scriptId; + mov edx, 1; + call scr_new_; + mov tmp, eax; + } + if (tmp == -1) + return; + __asm { + mov eax, scriptId; + lea edx, scriptPtr; + call scr_ptr_; + mov tmp, eax; + } + if (tmp == -1) + return; + // fill spatial script properties: + *(DWORD*)(scriptPtr + 0x14) = scriptIndex - 1; + *(DWORD*)(scriptPtr + 0x8) = (elevation << 29) & 0xE0000000 | tile; + *(DWORD*)(scriptPtr + 0xC) = radius; + // this will load appropriate script program and link it to the script instance we just created: + __asm { + mov eax, scriptId; + mov edx, 1; // start_p_proc + call exec_script_proc_; + mov eax, scriptPtr; + mov eax, [eax + 0x18]; // program pointer + call scr_find_obj_from_program_; + mov objectPtr, eax; + } + SetOpReturn((int)objectPtr); +} + +static void __declspec(naked) op_create_spatial() { + _WRAP_OPCODE(4, op_create_spatial2) +} +static void __declspec(naked) op_spatial_radius() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ecx, eax) + _CHECK_ARG_INT(cx, fail) + __asm { + test eax, eax; + js fail; + mov eax, [eax+0xC]; +fail: + } + _RET_VAL_INT(ebp) + _OP_END +} + +static void __declspec(naked) GetScript() { + __asm { + pushad; + mov ecx, eax; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xc001; + jnz fail; + test eax, eax; + jz fail; + mov edx, [eax+0x80]; + cmp edx, -1; + jz fail; + inc edx; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ecx; + call SetResult; + mov eax, ecx; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} + +static void __declspec(naked) set_critter_burst_disable() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + push ecx; + push eax; + call SetNoBurstMode; +end: + popad; + retn; + } +} +static void __declspec(naked) get_weapon_ammo_pid() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + test eax, eax; + jz fail; + mov edx, [eax+0x40]; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) set_weapon_ammo_pid() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + test eax, eax; + jz end; + mov [eax+0x40], ecx; +end: + popad; + retn; + } +} +static void __declspec(naked) get_weapon_ammo_count() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + test eax, eax; + jz fail; + mov edx, [eax+0x3c]; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) set_weapon_ammo_count() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + mov ecx, eax; + mov eax, ebp; + call GetArgType; + mov esi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + cmp si, 0xc001; + jnz end; + test eax, eax; + jz end; + mov [eax+0x3c], ecx; +end: + popad; + retn; + } +} + +static const DWORD obj_blocking_at_ = 0x48B848; // (int aExcludeObject /* can be 0 */, signed int aTile, int aElevation) +static const DWORD obj_shoot_blocking_at_ = 0x48B930; // +static const DWORD obj_ai_blocking_at_ = 0x48BA20; // +static const DWORD obj_scroll_blocking_at_ = 0x48BB44; // +static const DWORD obj_sight_blocking_at = 0x48BB88; // +static const DWORD make_straight_path_func_ = 0x4163C8; // (TGameObj *aObj, int aTileFrom, int a3, signed int aTileTo, TGameObj **aObjResult, int a5, int (*a6)(void)) +// (int aObjFrom, int aTileFrom, char* aPathPtr, int aTileTo, int a5, int (__fastcall *a6)(_DWORD, _DWORD)) +// - path is saved in ecx as a sequence of tile directions (0..5) to move on each step, +// - returns path length +static const DWORD make_path_func_ = 0x415EFC; + +static DWORD _stdcall obj_blocking_at_wrapper(DWORD obj, DWORD tile, DWORD elevation, DWORD func) { + DWORD retval; + __asm { + mov eax, obj; + mov edx, tile; + mov ebx, elevation; + call func; + mov retval, eax; + } + return retval; +} + +static DWORD _stdcall make_straight_path_func_wrapper(DWORD obj, DWORD tileFrom, DWORD a3, DWORD tileTo, DWORD* result, DWORD a6, DWORD func) { + DWORD retval; + __asm { + mov eax, obj; + mov edx, tileFrom; + mov ecx, a3; + mov ebx, tileTo; + push func; + push a6; + push result; + call make_straight_path_func_; + mov retval, eax; + } + return retval; +} + +#define BLOCKING_TYPE_BLOCK (0) +#define BLOCKING_TYPE_SHOOT (1) +#define BLOCKING_TYPE_AI (2) +#define BLOCKING_TYPE_SIGHT (3) +#define BLOCKING_TYPE_SCROLL (4) + +static DWORD getBlockingFunc(DWORD type) { + switch (type) { + case BLOCKING_TYPE_BLOCK: default: + return obj_blocking_at_; + case BLOCKING_TYPE_SHOOT: + return obj_shoot_blocking_at_; + case BLOCKING_TYPE_AI: + return obj_ai_blocking_at_; + case BLOCKING_TYPE_SIGHT: + return obj_sight_blocking_at; + //case 4: + // return obj_scroll_blocking_at_; + + } +} + +static void _stdcall op_make_straight_path2() { + DWORD objFrom = GetOpArgInt(0), + tileTo = GetOpArgInt(1), + type = GetOpArgInt(2), + resultObj, arg6; + arg6 = (type == BLOCKING_TYPE_SHOOT) ? 32 : 0; + make_straight_path_func_wrapper(objFrom, *(DWORD*)(objFrom + 4), 0, tileTo, &resultObj, arg6, getBlockingFunc(type)); + SetOpReturn(resultObj, DATATYPE_INT); +} + +static void __declspec(naked) op_make_straight_path() { + _WRAP_OPCODE(3, op_make_straight_path2) +} + +static void _stdcall op_make_path2() { + DWORD objFrom = GetOpArgInt(0), + tileFrom = 0, + tileTo = GetOpArgInt(1), + type = GetOpArgInt(2), + func = getBlockingFunc(type), + arr; + long pathLength, a5 = 1; + if (!objFrom) { + SetOpReturn(0, DATATYPE_INT); + return; + } + tileFrom = *(DWORD*)(objFrom + 4); + char pathData[800]; + char* pathDataPtr = pathData; + __asm { + mov eax, objFrom; + mov edx, tileFrom; + mov ecx, pathDataPtr; + mov ebx, tileTo; + push func; + push a5; + call make_path_func_; + mov pathLength, eax; + } + arr = TempArray(pathLength, 0); + for (int i=0; i < pathLength; i++) { + arrays[arr].val[i].set((long)pathData[i]); + } + SetOpReturn(arr, DATATYPE_INT); +} + +static void __declspec(naked) op_make_path() { + _WRAP_OPCODE(3, op_make_path2) +} + + +static void _stdcall op_obj_blocking_at2() { + DWORD tile = GetOpArgInt(0), + elevation = GetOpArgInt(1), + type = GetOpArgInt(2), + resultObj; + resultObj = obj_blocking_at_wrapper(0, tile, elevation, getBlockingFunc(type)); + if (resultObj && type == BLOCKING_TYPE_SHOOT && (*(DWORD*)(resultObj + 39) & 0x80)) { // don't know what this flag means, copy-pasted from the engine code + // this check was added because the engine always does exactly this when using shoot blocking checks + resultObj = 0; + } + SetOpReturn(resultObj, DATATYPE_INT); +} + +static void __declspec(naked) op_obj_blocking_at() { + _WRAP_OPCODE(3, op_obj_blocking_at2) +} + +static void _stdcall op_tile_get_objects2() { + DWORD tile = GetOpArgInt(0), + elevation = GetOpArgInt(1), + obj; + DWORD arrayId = TempArray(0, 4); + __asm { + mov eax, elevation; + mov edx, tile; + call obj_find_first_at_tile_; + mov obj, eax; + } + while (obj) { + arrays[arrayId].push_back((long)obj); + __asm { + call obj_find_next_at_tile_; + mov obj, eax; + } + } + SetOpReturn(arrayId, DATATYPE_INT); +} + +static void __declspec(naked) op_tile_get_objects() { + _WRAP_OPCODE(2, op_tile_get_objects2) +} + +static const DWORD partyMemberListPtr = 0x519DA8; // each struct - 4 integers, first integer - objPtr +static const DWORD partyMemberCount = 0x519DAC; + +static void _stdcall op_get_party_members2() { + DWORD obj, mode = GetOpArgInt(0), isDead; + int i, actualCount = *(DWORD*)partyMemberCount; + DWORD arrayId = TempArray(0, 4); + DWORD* partyMemberList = *(DWORD**)partyMemberListPtr; + for (i = 0; i < actualCount; i++) { + obj = partyMemberList[i*4]; + if (mode == 0) { // mode 0 will act just like op_party_member_count in fallout2 + if ((*(DWORD*)(obj + 100) >> 24) != 1) // obj type != critter + continue; + __asm { + mov eax, obj; + call critter_is_dead_; + mov isDead, eax; + } + if (isDead) + continue; + if (*(DWORD*)(obj + 36) & 1) // no idea.. + continue; + } + arrays[arrayId].push_back((long)obj); + } + SetOpReturn(arrayId, DATATYPE_INT); +} + +static void __declspec(naked) op_get_party_members() { + _WRAP_OPCODE(1, op_get_party_members2) +} + + +static void __declspec(naked) op_art_exists() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ecx, eax) + _CHECK_ARG_INT(cx, fail) + __asm { + call art_exists_; + jmp end; +fail: + xor eax, eax; +end: + } + _RET_VAL_INT(ebp) + _OP_END +} + +static void _stdcall op_obj_is_carrying_obj2() { + DWORD num = 0; + if (IsOpArgInt(0) && IsOpArgInt(1)) { + TGameObj *invenObj = (TGameObj*)GetOpArgInt(0), + *itemObj = (TGameObj*)GetOpArgInt(1); + if (invenObj && itemObj) { + for (int i = 0; i < invenObj->invenCount; i++) { + if (invenObj->invenTablePtr[i].object == itemObj) { + num = invenObj->invenTablePtr[i].count; + break; + } + } + } + } + SetOpReturn(num, DATATYPE_INT); +} + +static void __declspec(naked) op_obj_is_carrying_obj() { + _WRAP_OPCODE(2, op_obj_is_carrying_obj2) +} \ No newline at end of file diff --git a/sfall/ScriptOps/PerksOp.hpp b/sfall/ScriptOps/PerksOp.hpp new file mode 100644 index 00000000..9b545b04 --- /dev/null +++ b/sfall/ScriptOps/PerksOp.hpp @@ -0,0 +1,668 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "Perks.h" +#include "ScriptExtender.h" + +static void __declspec(naked) GetPerkOwed() { + __asm { + pushad; + mov ecx, eax; + movzx edx, byte ptr ds:[0x570A29]; + call SetResult; + mov edx, 0xc001; + mov eax, ecx; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) SetPerkOwed() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + and eax, 0xff; + cmp eax, 250; + jg end; + mov byte ptr ds:[0x570A29], al +end: + popad + retn; + } +} +static void __declspec(naked) set_perk_freq() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + call SetPerkFreq; +end: + popad + retn; + } +} +static void __declspec(naked) GetPerkAvailable() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz fail; + cmp eax, 119; + jge fail; + mov edx, eax; + mov eax, 0x006610B8; + mov ebx, 0x00496B44; + call ebx; + mov edx, eax; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ecx + call SetResult; + mov edx, 0xc001; + mov eax, ecx; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) funcSetPerkName() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edi, eax; + mov eax, ecx; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp si, 0x9001; + jz next; + cmp si, 0x9801; + jnz end; +next: + mov ebx, edi; + mov edx, esi; + mov esi, eax; + mov eax, ecx; + mov edi, 0x4678E0; + call edi; + push eax; + push esi; + call SetPerkName; + jmp end; +end: + popad; + retn; + } +} +static void __declspec(naked) funcSetPerkDesc() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edi, eax; + mov eax, ecx; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp si, 0x9001; + jz next; + cmp si, 0x9801; + jnz end; +next: + mov ebx, edi; + mov edx, esi; + mov esi, eax; + mov eax, ecx; + mov edi, 0x4678E0; + call edi; + push eax; + push esi; + call SetPerkDesc; + jmp end; +end: + popad; + retn; + } +} +static void __declspec(naked) funcSetPerkValue() { + __asm { + pushad; + sub edx, 0x5e0-8; // offset of value into perk struct; edx = ((edx/4) - 0x178 + 0x8) * 4 + push edx; + mov ecx, eax; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + mov edi, eax; + mov eax, ecx; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz fail; + cmp si, 0xC001; + jnz fail; + push edi; + push eax; + call SetPerkValue; + jmp end; +fail: + add esp, 4; +end: + popad; + retn; + } +} + +static void __declspec(naked) fSetSelectablePerk() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + + movzx eax, word ptr [esp+12]; + cmp eax, 0xc001; + jne fail; + movzx eax, word ptr [esp+20]; + cmp eax, 0xc001; + jne fail; + movzx eax, word ptr ds:[esp+4]; + cmp eax, 0x9001; + je next1; + cmp eax, 0x9801; + jne fail; +next1: + movzx eax, word ptr ds:[esp+28]; + cmp eax, 0x9001; + je next2; + cmp eax, 0x9801; + jne fail; +next2: + mov eax, ecx; + mov edx, [esp+28]; + mov ebx, [esp+24]; + call GetStringVar; + push eax; + mov eax, [esp+20]; + push eax; + mov eax, [esp+16]; + push eax; + mov eax, ecx; + mov edx, [esp+16]; + mov ebx, [esp+12]; + call GetStringVar; + push eax; + + call SetSelectablePerk; +fail: + add esp, 32; + popad; + retn; + } +} +static void __declspec(naked) fSetFakePerk() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + + movzx eax, word ptr [esp+12]; + cmp eax, 0xc001; + jne fail; + movzx eax, word ptr [esp+20]; + cmp eax, 0xc001; + jne fail; + movzx eax, word ptr ds:[esp+4]; + cmp eax, 0x9001; + je next1; + cmp eax, 0x9801; + jne fail; +next1: + movzx eax, word ptr ds:[esp+28]; + cmp eax, 0x9001; + je next2; + cmp eax, 0x9801; + jne fail; +next2: + mov eax, ecx; + mov edx, [esp+28]; + mov ebx, [esp+24]; + call GetStringVar; + push eax; + mov eax, [esp+20]; + push eax; + mov eax, [esp+16]; + push eax; + mov eax, ecx; + mov edx, [esp+16]; + mov ebx, [esp+12]; + call GetStringVar; + push eax; + + call SetFakePerk; +fail: + add esp, 32; + popad; + retn; + } +} +static void __declspec(naked) fSetFakeTrait() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + push eax; + + movzx eax, word ptr [esp+12]; + cmp eax, 0xc001; + jne fail; + movzx eax, word ptr [esp+20]; + cmp eax, 0xc001; + jne fail; + movzx eax, word ptr ds:[esp+4]; + cmp eax, 0x9001; + je next1; + cmp eax, 0x9801; + jne fail; +next1: + movzx eax, word ptr ds:[esp+28]; + cmp eax, 0x9001; + je next2; + cmp eax, 0x9801; + jne fail; +next2: + mov eax, ecx; + mov edx, [esp+28]; + mov ebx, [esp+24]; + call GetStringVar; + push eax; + mov eax, [esp+20]; + push eax; + mov eax, [esp+16]; + push eax; + mov eax, ecx; + mov edx, [esp+16]; + mov ebx, [esp+12]; + call GetStringVar; + push eax; + + call SetFakeTrait; +fail: + add esp, 32; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetPerkboxTitle() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call ebx; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, GetStringVar; + call ecx; + push eax; + call SetPerkboxTitle; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fIgnoreDefaultPerks() { + __asm { + push ebx; + push ecx; + push edx; + call IgnoreDefaultPerks; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fRestoreDefaultPerks() { + __asm { + push ebx; + push ecx; + push edx; + call RestoreDefaultPerks; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fClearSelectablePerks() { + __asm { + push ebx; + push ecx; + push edx; + call ClearSelectablePerks; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fHasFakePerk() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, GetStringVar; + call ecx; + push eax; + call HasFakePerk; +end: + mov edx, eax; + mov eax, edi; + call SetResult; + mov eax, edi; + mov edx, 0xc001; + call SetResultType; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fHasFakeTrait() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + call GetArgType; + mov edx, eax; + mov eax, edi; + mov ebx, 0x467500; + call GetArg; + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz end; +next: + mov ebx, eax; + mov eax, edi; + mov ecx, GetStringVar; + call ecx; + push eax; + call HasFakeTrait; +end: + mov edx, eax; + mov eax, edi; + call SetResult; + mov eax, edi; + mov edx, 0xc001; + call SetResultType; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fAddPerkMode() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push eax; + call AddPerkMode; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) remove_trait() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + xor ebx, ebx; + dec ebx; + mov ecx, ds:[0x66BE44]; + cmp eax, ds:[0x66BE40]; + jne next; + mov ds:[0x66BE40], ecx; + mov ds:[0x66BE44], ebx; + jmp end; +next: + cmp eax, ds:[0x66BE44]; + jne end; + mov ds:[0x66BE44], ebx; +end: + popad; + retn; + } +} + +static void __declspec(naked) SetPyromaniacMod() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + push 0x424AB6; + call SafeWrite8; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} + +static void __declspec(naked) fApplyHeaveHoFix() { + __asm { + push ebx; + push ecx; + push edx; + call ApplyHeaveHoFix; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetSwiftLearnerMod() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + push eax; + push 0x4AFAE2; + call SafeWrite32; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/ScriptArrays.hpp b/sfall/ScriptOps/ScriptArrays.hpp new file mode 100644 index 00000000..05afe402 --- /dev/null +++ b/sfall/ScriptOps/ScriptArrays.hpp @@ -0,0 +1,666 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +static void __declspec(naked) create_array() { + __asm { + pushad; + mov edi, eax; + call GetArgType + mov ebx, eax; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + cmp bx, 0xc001; + jne fail; + cmp dx, 0xc001; + jne fail; + push ecx; + push eax; + call CreateArray; + mov edx, eax; + jmp end; +fail: + xor edx, edx; +end: + mov eax, edi; + call SetResult; + mov edx, 0xc001; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) set_array() { + __asm { + pushad; + mov ebp, eax; + //Get args + call GetArgType + mov edx, eax; + mov eax, ebp; + call GetArg; + mov edi, eax; // value + mov eax, ebp; + call GetArgType + mov ecx, eax; + mov eax, ebp; + call GetArg; + mov esi, eax; // key + mov eax, ebp; + call GetArgType + mov ebx, eax; + mov eax, ebp; + call GetArg; + xchg eax, edi; // arrayID + //Error check: + cmp bx, 0xC001; + jne end; + push 1; // arg 6: allow unset + push edx; // arg 5: value type + // value: + cmp dx, 0x9001; + jz next; + cmp dx, 0x9801; + jnz notstring; +next: + mov ebx, eax; + mov eax, ebp; + call GetStringVar; +notstring: + push eax; // arg 4: value + // key: + cmp cx, 0x9001; + jz next1; + cmp cx, 0x9801; + jnz notstring1; +next1: + mov edx, ecx; + mov ebx, esi; + mov eax, ebp; + call GetStringVar; + mov esi, eax; +notstring1: + push ecx; // arg 3: key type + push esi; // arg 2: key + push edi; // arg 1: arrayID + call SetArray +end: + popad; + retn; + } +} +/* + used in place of [] operator when compiling in sslc + so it works as get_array if first argument is int and as substr(x, y, 1) if first argument is string +*/ +static void __declspec(naked) get_array() { + __asm { + pushad; + mov ebp, eax; + //Get args + call GetArgType + mov ebx, eax; + mov eax, ebp; + call GetArg; + mov edi, eax; + mov eax, ebp; + call GetArgType + mov ecx, eax; + mov eax, ebp; + call GetArg; + mov esi, eax; + mov eax, ebp; + // check first argument type + cmp cx, VAR_TYPE_STR; + je callsubstr; + cmp cx, VAR_TYPE_STR2; + jne proceedgetarray; +callsubstr: + // use substr instead of get_array when used on string + // in this case, check for second argument to be numeric + cmp bx, VAR_TYPE_INT; + jne fail; + mov eax, ebp; + mov ebx, esi; + mov edx, ecx; + call GetStringVar; + push 1; + push edi; + push eax; + call mysubstr; + mov edx, eax; // result substring + mov ebx, VAR_TYPE_STR; // result type + jmp end; + +proceedgetarray: + cmp cx, VAR_TYPE_INT; // only int is allowed for arrayID in this case + jne fail; + cmp bx, VAR_TYPE_STR; + je next1; + cmp bx, VAR_TYPE_STR2; + jne notstring1; +next1: + mov ecx, ebx; + mov edx, ebx; + mov ebx, edi; + mov eax, ebp; + call GetStringVar; + mov edi, eax; + mov ebx, ecx; +notstring1: + mov eax, esp; // ptr to resultType (will be changed in GetArray) + push eax; + push ebx; + push edi; + push esi; + call GetArray; + mov edx, eax; // result data + mov ebx, [esp]; // resultType + jmp end; +fail: + xor edx, edx; + mov ebx, 0xc001; +end: + cmp bx, 0x9801; + jne notstring; + mov eax, ebp; + call CreateStringVar; + mov edx, eax; +notstring: + mov eax, ebp; + call SetResult; + mov edx, ebx; + mov eax, ebp; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) free_array() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + cmp bx, 0xc001; + jne end; + push eax; + call FreeArray; +end: + popad; + retn; + } +} +static void __declspec(naked) len_array() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + cmp bx, 0xc001; + jne fail; + push eax; + call LenArray; + mov edx, eax; + jmp end; +fail: + xor edx, edx; +end: + mov eax, edi; + call SetResult; + mov edx, 0xc001; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) resize_array() { + __asm { + pushad; + mov ebp, eax; + //Get args + call GetArgType + mov ebx, eax; + mov eax, ebp; + call GetArg; + mov edi, eax; + mov eax, ebp; + call GetArgType + mov ecx, eax; + mov eax, ebp; + call GetArg; + mov esi, eax; + mov eax, ebp; + //Error check + cmp bx, 0xc001; + jne end; + cmp cx, 0xc001; + jne end; + push edi; + push esi; + call ResizeArray; +end: + popad; + retn; + } +} +static void __declspec(naked) temp_array() { + __asm { + pushad; + mov edi, eax; + call GetArgType + mov ebx, eax; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; + cmp bx, 0xc001; + jne fail; + cmp dx, 0xc001; + jne fail; + push ecx; + push eax; + call TempArray; + mov edx, eax; + jmp end; +fail: + xor edx, edx; +end: + mov eax, edi; + call SetResult; + mov edx, 0xc001; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) fix_array() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov ebx, eax; + mov eax, edi; + call GetArg; + cmp bx, 0xc001; + jne end; + push eax; + call FixArray; +end: + popad; + retn; + } +} +static void __declspec(naked) scan_array() { + __asm { + pushad; + mov ebp, eax; + //Get args + call GetArgType + mov edx, eax; + mov eax, ebp; + call GetArg; + mov edi, eax; // value (needle) + mov eax, ebp; + call GetArgType + mov ecx, eax; + mov eax, ebp; + call GetArg; + mov esi, eax; // arrayID (haystack) + mov eax, ebp; + //Error check + cmp cx, VAR_TYPE_INT; + jne fail; + cmp dx, VAR_TYPE_STR; + je getstringvar; + cmp dx, VAR_TYPE_STR2; + jne success; +getstringvar: + mov ebx, edi; + mov eax, ebp; + call GetStringVar; + mov edi, eax; +success: + push esp; + push edx; + push edi; + push esi; + call ScanArray; + mov edx, eax; // result + mov ebx, [esp]; // resultType + jmp end; +fail: + mov ebx, VAR_TYPE_INT; + xor edx, edx; + dec edx; // returns -1 +end: + cmp ebx, VAR_TYPE_STR; + jne resultnotstr; + mov eax, ebp; + call CreateStringVar; + mov edx, eax; // str ptr +resultnotstr: + mov eax, ebp; + call SetResult; + mov edx, ebx; + mov eax, ebp; + call SetResultType; + popad; + retn; + } +} + + +static void __declspec(naked) op_save_array() { + _OP_BEGIN(ebp) + // Get args + _GET_ARG_R32(ebp, ebx, edi) + _GET_ARG_R32(ebp, edx, ecx) + __asm { + // arg check: + cmp bx, VAR_TYPE_INT + jne end + } + _CHECK_PARSE_STR(1, ebp, dx, ecx) + __asm { + push edi // arg 3: arrayID + push edx // arg 2: keyType + push ecx // arg 1: key + call SaveArray + } +end: + _OP_END +} + +static void __declspec(naked) op_load_array() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, edx, ecx) + _CHECK_PARSE_STR(1, ebp, dx, ecx) + __asm { + push edx; // arg 2: keyType + push ecx; // arg 1: key + call LoadArray; + } + _RET_VAL_INT(ebp) + _OP_END +} + +static void __declspec(naked) op_get_array_key() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, edx, ecx) // index + _GET_ARG_R32(ebp, ebx, edi) // arrayID + __asm { + // arg check: + cmp bx, VAR_TYPE_INT; + jne wrongarg; + cmp dx, VAR_TYPE_INT; + jne wrongarg; + push esp; // arg 3: *resultType + push ecx; // arg 2: index + push edi; // arg 1: arrayID + call GetArrayKey; + } + _RET_VAL_POSSIBLY_STR(1, ebp, [esp]) + goto end; + __asm { +wrongarg: + xor eax, eax; // return 0 on wrong arguments + } + _RET_VAL_INT(ebp) +end: + _OP_END +} + +static void __declspec(naked) op_stack_array() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, edx, esi) // value + _GET_ARG_R32(ebp, ebx, edi) // key + __asm { + push edx // arg 4: valueType + push ebx + } + _CHECK_PARSE_STR(1, ebp, dx, esi) + __asm { + pop ebx + mov ecx, ebx + } + _CHECK_PARSE_STR(2, ebp, bx, edi) + __asm { + push esi // arg 3: value + push ecx // arg 2: keyType + push edi // arg 1: key + call StackArray + } + _RET_VAL_INT(ebp) + _OP_END +} + +// object LISTS + +struct sList { + DWORD* obj; + DWORD len; + DWORD pos; + + sList(const std::vector* vec) { + len=vec->size(); + obj=new DWORD[len]; + for(DWORD i=0;i& vec) { + if (type == 6) { + DWORD scriptPtr, self_obj, programPtr; + for (int elev=0; elev<=2; elev++) { + __asm { + mov eax, elev; + call scr_find_first_at_; + mov scriptPtr, eax; + } + while (scriptPtr != 0) { + self_obj = *(DWORD*)(scriptPtr + 0x34); + if (self_obj == 0) { + programPtr = *(DWORD*)(scriptPtr + 0x18); + __asm { + mov eax, programPtr; + call scr_find_obj_from_program_; + mov self_obj, eax; + } + } + vec.push_back(self_obj); + __asm { + call scr_find_next_at_; + mov scriptPtr, eax; + } + } + } + } else if (type == 4) { + DWORD** squares=(DWORD**)0x66BE08; + for(int elv=0;elv<2;elv++) { + DWORD* esquares=squares[elv]; + for(int tile=0;tile<10000;tile++) { + esquares[tile]=0x8f000002; + } + } + } else { + for(int elv=0;elv<3;elv++) { + for(int tile=0;tile<40000;tile++) { + DWORD obj; + __asm { + mov edx, tile; + mov eax, elv; + call obj_find_first_at_tile_; + mov obj, eax; + } + while(obj) { + DWORD otype = ((DWORD*)obj)[25]; + otype = (otype&0xff000000) >> 24; + if(type==9 || (type==0&&otype==1) || (type==1&&otype==0) || (type>=2&&type<=5&&type==otype)) vec.push_back(obj); + __asm { + call obj_find_next_at_tile_; + mov obj, eax; + } + } + } + } + } +} +static void* _stdcall list_begin2(DWORD type) { + std::vector vec = std::vector(); + FillListVector(type, vec); + sList* list=new sList(&vec); + return list; +} +static DWORD _stdcall list_as_array2(DWORD type) { + std::vector vec = std::vector(); + FillListVector(type, vec); + DWORD id=TempArray(vec.size(), 4); + for(DWORD i=0;ipos==list->len) return 0; + else return list->obj[list->pos++]; +} +static void _stdcall list_end2(sList* list) { + delete[] list->obj; + delete list; +} +static void __declspec(naked) list_begin() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call list_begin2; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) list_as_array() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call list_as_array2; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) list_next() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz fail; + push eax; + call list_next2; + mov edx, eax; + jmp end; +fail: + xor edx, edx; + dec edx; +end: + mov eax, ebp; + call SetResult; + mov eax, ebp; + mov edx, 0xc001; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) list_end() { + __asm { + pushad; + mov ebp, eax; + call GetArgType; + mov edi, eax; + mov eax, ebp; + call GetArg; + cmp di, 0xc001; + jnz end; + push eax; + call list_end2; +end: + popad; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/ScriptUtils.hpp b/sfall/ScriptOps/ScriptUtils.hpp new file mode 100644 index 00000000..69a3f74e --- /dev/null +++ b/sfall/ScriptOps/ScriptUtils.hpp @@ -0,0 +1,877 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2011, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "main.h" + +#include "ScriptExtender.h" +#include "ScriptArrays.hpp" +#include "FileSystem.h" +#include "Arrays.h" + +static void __declspec(naked) funcSqrt() { + __asm { + pushad; + sub esp, 4; + mov ecx, eax; + call GetArgType; + mov ebx, eax; + mov eax, ecx; + call GetArg; + cmp bx, 0xc001; + jnz arg1l2; + mov [esp], eax; + fild [esp]; + jmp calc; +arg1l2: + cmp bx, 0xa001; + jnz fail; + mov [esp], eax; + fld [esp]; +calc: + fsqrt; + fstp [esp]; + mov edx, [esp]; + jmp end; +fail: + fldz; + fstp [esp]; + mov edx, [esp]; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xa001; + mov eax, ecx; + call SetResultType; + add esp, 4; + popad; + retn; + } +} +static void __declspec(naked) funcAbs() { + __asm { + pushad; + sub esp, 4; + mov ecx, eax; + call GetArgType; + mov ebx, eax; + mov eax, ecx; + call GetArg; + cmp bx, 0xc001; + jnz arg1l2; + mov [esp], eax; + fild [esp]; + jmp calc; +arg1l2: + cmp bx, 0xa001; + jnz fail; + mov [esp], eax; + fld [esp]; +calc: + fabs; + fstp [esp]; + mov edx, [esp]; + jmp end; +fail: + fldz; + fstp [esp]; + mov edx, [esp]; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xa001; + mov eax, ecx; + call SetResultType; + add esp, 4; + popad; + retn; + } +} +static void __declspec(naked) funcSin() { + __asm { + pushad; + sub esp, 4; + mov ecx, eax; + call GetArgType; + mov ebx, eax; + mov eax, ecx; + call GetArg; + cmp bx, 0xc001; + jnz arg1l2; + mov [esp], eax; + fild [esp]; + jmp calc; +arg1l2: + cmp bx, 0xa001; + jnz fail; + mov [esp], eax; + fld [esp]; +calc: + fsin; + fstp [esp]; + mov edx, [esp]; + jmp end; +fail: + fldz; + fstp [esp]; + mov edx, [esp]; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xa001; + mov eax, ecx; + call SetResultType; + add esp, 4; + popad; + retn; + } +} +static void __declspec(naked) funcCos() { + __asm { + pushad; + sub esp, 4; + mov ecx, eax; + call GetArgType; + mov ebx, eax; + mov eax, ecx; + call GetArg; + cmp bx, 0xc001; + jnz arg1l2; + mov [esp], eax; + fild [esp]; + jmp calc; +arg1l2: + cmp bx, 0xa001; + jnz fail; + mov [esp], eax; + fld [esp]; +calc: + fcos; + fstp [esp]; + mov edx, [esp]; + jmp end; +fail: + fldz; + fstp [esp]; + mov edx, [esp]; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xa001; + mov eax, ecx; + call SetResultType; + add esp, 4; + popad; + retn; + } +} +static void __declspec(naked) funcTan() { + __asm { + pushad; + sub esp, 4; + mov ecx, eax; + call GetArgType; + mov ebx, eax; + mov eax, ecx; + call GetArg; + cmp bx, 0xc001; + jnz arg1l2; + mov [esp], eax; + fild [esp]; + jmp calc; +arg1l2: + cmp bx, 0xa001; + jnz fail; + mov [esp], eax; + fld [esp]; +calc: + fptan; + fstp [esp]; + fstp [esp]; + mov edx, [esp]; + jmp end; +fail: + fldz; + fstp [esp]; + mov edx, [esp]; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xa001; + mov eax, ecx; + call SetResultType; + add esp, 4; + popad; + retn; + } +} +static void __declspec(naked) funcATan() { + __asm { + pushad; + sub esp, 4; + mov ecx, eax; + call GetArgType; + mov ebx, eax; + mov eax, ecx; + call GetArg; + mov edi, eax; + mov eax, ecx; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + cmp bx, 0xc001; + jnz arg1l2; + mov [esp], eax; + fild [esp]; + jmp arg2l1; +arg1l2: + cmp bx, 0xa001; + jnz fail; + mov [esp], eax; + fld [esp]; +arg2l1: + cmp dx, 0xc001; + jnz arg2l2; + mov [esp], edi; + fild [esp]; + jmp calc; +arg2l2: + cmp dx, 0xa001; + jnz fail2; + mov [esp], edi; + fld [esp]; +calc: + fpatan; + fstp [esp]; + mov edx, [esp]; + jmp end; +fail2: + fstp [esp]; +fail: + fldz; + fstp [esp]; + mov edx, [esp]; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xa001; + mov eax, ecx; + call SetResultType; + add esp, 4; + popad; + retn; + } +} + +static int _stdcall StringSplit(const char* str, const char* split) { + int id; + if(strlen(split)==0) { + id=TempArray(strlen(str), 4); + for(DWORD i=0;i= srclen) + length = 0; + else if (length+pos > srclen) + length = srclen-pos; + newstr = new char[length+1]; + if (length > 0) + memcpy(newstr, &str[pos], length); + newstr[length] = '\0'; + return newstr; +} + +static DWORD _stdcall mystrlen(char* str) { + return strlen(str); +} +static char* sprintfbuf = NULL; +static char* _stdcall mysprintf(char* format, DWORD value, DWORD valueType) { + valueType = valueType & 0xFFFF; // use lower 2 bytes + int fmtlen = strlen(format); + int buflen = fmtlen + 1; + for (int i=0; i= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + if (c == 'h' || c == 'l' || c == 'j' || c == 'z' || c == 't' || c == 'L') // ignore sub-specifiers + continue; + if (c == 's' && valueType != VAR_TYPE_STR2 && valueType != VAR_TYPE_STR) // don't allow to treat non-string values as string pointers + c = 'd'; + else if (c == 'n') // don't allow "n" specifier + c = 'd'; + specifier = c; + mode = 2; + } else if (c == '%') { + mode = 0; + hasDigits = false; + } else if (c >= '0' && c <= '9') { + hasDigits = true; + } + break; + case 2: // postfix + default: + if (c == '%') { // don't allow more than one specifier + newfmt[j++] = '%'; // escape it + if (format[i+1] == '%') + i++; // skip already escaped + } + break; + } + newfmt[j++] = c; + } + newfmt[j] = '\0'; + // calculate required memory + if (hasDigits) { + buflen = 254; + } else if (specifier == 'c') { + buflen = j; + } else if (specifier == 's') { + buflen = j + strlen((char*)value); + } else { + buflen = j + 30; // numbers + } + if (sprintfbuf) + delete[] sprintfbuf; + sprintfbuf = new char[buflen+1]; + if (valueType == VAR_TYPE_FLOAT) { + _snprintf(sprintfbuf, buflen, newfmt, *(float*)(&value)); + } else { + _snprintf(sprintfbuf, buflen, newfmt, value); + } + sprintfbuf[buflen] = '\0'; // just in case + delete[] newfmt; + return sprintfbuf; +} + +static void __declspec(naked) op_substr() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + push eax; + mov eax, edi; + call GetArg; // length + push eax; + mov eax, edi; + call GetArgType; + push eax; + mov eax, edi; + call GetArg; // position + push eax; + mov eax, edi; + call GetArgType; + push eax; + mov eax, edi; + call GetArg; // string + push eax; + + movzx eax, word ptr [esp+12]; + cmp eax, VAR_TYPE_INT; + jne fail; + movzx eax, word ptr [esp+20]; + cmp eax, VAR_TYPE_INT; + jne fail; + movzx eax, word ptr [esp+4]; + cmp eax, VAR_TYPE_STR2; + je next1; + cmp eax, VAR_TYPE_STR; + jne fail; +next1: + mov eax, edi; + mov edx, [esp+4]; + mov ebx, [esp]; + call GetStringVar; + mov ebx, [esp+16]; + mov edx, [esp+8]; + push ebx; + push edx; + push eax; + call mysubstr; + mov edx, eax; + mov eax, edi; + call CreateStringVar; + mov edx, eax; + mov eax, edi; + call SetResult; + mov edx, VAR_TYPE_STR; + mov eax, edi; + call SetResultType; + jmp end; +fail: + xor edx, edx; + mov eax, edi; + call SetResult; + mov edx, VAR_TYPE_INT; + mov eax, edi; + call SetResultType; +end: + add esp, 24; + popad; + retn; + } +} + +static void __declspec(naked) op_strlen() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; // string + cmp dx, VAR_TYPE_STR2; + je next; + cmp dx, VAR_TYPE_STR; + jne fail; +next: + mov ebx, eax; + mov eax, edi; + call GetStringVar; + push eax; + call mystrlen; + jmp end; +fail: + xor eax, eax; // return 0 +end: + mov edx, eax; + mov eax, edi; + call SetResult; + mov edx, VAR_TYPE_INT; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} + +static void __declspec(naked) op_sprintf() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; // any value + mov ebx, eax; + mov eax, edi; + + call GetArgType; + mov ecx, eax; + mov eax, edi; + call GetArg; // format string + mov esi, eax; + // check types + cmp cx, 0x9001; + je nextarg; + cmp cx, VAR_TYPE_STR; + jne fail; +nextarg: + cmp dx, 0x9001; + je next2; + cmp dx, VAR_TYPE_STR; + jne notstring; +next2: + mov eax, edi; + call GetStringVar; // value string ptr + mov ebx, eax; +notstring: + push edx; // arg 3 - valueType + mov eax, esi; + mov esi, ebx; + mov edx, ecx; + mov ebx, eax; + mov eax, edi; + call GetStringVar; // format string ptr + push esi; // arg 2 - value + push eax; // arg 1 - format str + call mysprintf; + mov edx, eax; + mov eax, edi; + call CreateStringVar; + mov edx, eax; + mov eax, edi; + call SetResult; + mov edx, VAR_TYPE_STR; + mov eax, edi; + call SetResultType; +fail: + popad; + retn; + } +} + +static void __declspec(naked) op_ord() { + _OP_BEGIN(ebp) + _GET_ARG_R32(ebp, ebx, esi) + _PARSE_STR_ARG(1, ebp, bx, esi, notstring) + __asm { + mov eax, 0; + mov al, [esi]; // first character + jmp done; +notstring: + mov eax, 0; + done: + } + _RET_VAL_INT(ebp) + _OP_END +} + +static DWORD _stdcall GetValueType(DWORD datatype) { + datatype&=0xffff; + switch(datatype) { + case VAR_TYPE_STR: + case VAR_TYPE_STR2: + return DATATYPE_STR; + case VAR_TYPE_INT: + return DATATYPE_INT; + case VAR_TYPE_FLOAT: + return DATATYPE_FLOAT; + default: + return DATATYPE_NONE; // just in case + } +} + +static void __declspec(naked) op_typeof() { + __asm { + pushad; + mov edi, eax; + call GetArgType; + mov edx, eax; + mov eax, edi; + call GetArg; // call just in case (not used) + push edx; + call GetValueType; + mov edx, eax; + mov eax, edi; + call SetResult; + mov edx, VAR_TYPE_INT; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} + +// fix for vanilla negate operator not working on floats +static const DWORD NegateFixHook_Back = 0x46AB79; +static void __declspec(naked) NegateFixHook() { + __asm { + mov ebx, edi; + lea edx, [ecx+36]; + mov eax, [ecx+28]; + cmp si, VAR_TYPE_FLOAT; + jne notfloat; + push ebx; + fld [esp]; + fchs + fstp [esp]; + pop ebx; + call pushShortStack + mov edx, VAR_TYPE_FLOAT + jmp end +notfloat: + neg ebx + call pushShortStack + mov edx, VAR_TYPE_INT +end: + mov eax, ecx + jmp NegateFixHook_Back; + } +} + +static void funcPow2() { + /*char buf[1024]; + sprintf(buf, "Pow! %d %d %d %d", opArgs[0], opArgTypes[0], opArgs[1], opArgTypes[1]); + DisplayConsoleMessage(buf);*/ + float base, result = 0.0; + if (!IsOpArgStr(0) && !IsOpArgStr(1)) { + base = GetOpArgFloat(0); + if (IsOpArgFloat(1)) + result = pow(base, GetOpArgFloat(1)); + else + result = pow(base, GetOpArgInt(1)); + + if (IsOpArgInt(0) && IsOpArgInt(1)) { + SetOpReturn((DWORD)(int)result, DATATYPE_INT); + } else { + SetOpReturn(result); + } + } else { + SetOpReturn(0, DATATYPE_INT); + } +} + +static void __declspec(naked) funcPow() { + _WRAP_OPCODE(2, funcPow2) +} + +static void funcLog2() { + SetOpReturn(log(GetOpArgFloat(0))); +} + +static void __declspec(naked) funcLog() { + _WRAP_OPCODE(1, funcLog2) +} + +static void funcExp2() { + SetOpReturn(exp(GetOpArgFloat(0))); +} + +static void __declspec(naked) funcExp() { + _WRAP_OPCODE(1, funcExp2) +} + +static void funcCeil2() { + SetOpReturn((int)ceil(GetOpArgFloat(0)), DATATYPE_INT); +} + +static void __declspec(naked) funcCeil() { + _WRAP_OPCODE(1, funcCeil2) +} + +static void funcRound2() { + float arg = GetOpArgFloat(0); + int argI = (int)arg; + float mod = arg - (float)argI; + if (abs(mod) >= 0.5) + argI += (mod > 0 ? 1 : -1); + SetOpReturn(argI, DATATYPE_INT); +} + +static void __declspec(naked) funcRound() { + _WRAP_OPCODE(1, funcRound2) +} + +/* + +*/ + +static const DWORD game_msg_files[] = + { 0x56D368 // COMBAT + , 0x56D510 // AI + , 0x56D754 // SCRNAME + , 0x58E940 // MISC + , 0x58EA98 // CUSTOM + , 0x59E814 // INVENTRY + , 0x59E980 // ITEM + , 0x613D28 // LSGAME + , 0x631D48 // MAP + , 0x6637E8 // OPTIONS + , 0x6642D4 // PERK + , 0x664348 // PIPBOY + , 0x664410 // QUESTS + , 0x6647FC // PROTO + , 0x667724 // SCRIPT + , 0x668080 // SKILL + , 0x6680F8 // SKILLDEX + , 0x66817C // STAT + , 0x66BE38 // TRAIT + , 0x672FB0 }; // WORLDMAP + + +static void _stdcall op_message_str_game2() { + DWORD fileId = opArgs[0]; + if (IsOpArgInt(0) && IsOpArgInt(1) && fileId < 20) { + int msgId = GetOpArgInt(1); + const char* msg = GetMessageStr(game_msg_files[fileId], msgId); + if (msg != 0) + SetOpReturn(msg); + else + SetOpReturn(0, DATATYPE_INT); + } else { + SetOpReturn(0, DATATYPE_INT); + } +} + +static void __declspec(naked) op_message_str_game() { + _WRAP_OPCODE(2, op_message_str_game2) +} \ No newline at end of file diff --git a/sfall/ScriptOps/StatsOp.hpp b/sfall/ScriptOps/StatsOp.hpp new file mode 100644 index 00000000..9229f956 --- /dev/null +++ b/sfall/ScriptOps/StatsOp.hpp @@ -0,0 +1,1323 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "stats.h" +#include "criticals.h" +#include "skills.h" +#include "Knockback.h" +#include "ScriptExtender.h" + +// stat_funcs +static void __declspec(naked) SetPCBaseStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + push esi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //eax now contains the stat ID, edi contains its new value + //Check args are valid + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + test eax, eax; + jl end; + cmp eax, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge end; + //set the new value + mov ds:[eax*4 + 0x51C394], edi; +end: + //Restore registers and return + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetPCExtraStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + push esi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //eax now contains the stat ID, edi contains its new value + //Check args are valid + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + test eax, eax; + jl end; + cmp eax, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge end; + //set the new value + mov ds:[eax*4 + 0x51C420], edi; +end: + //Restore registers and return + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetPCBaseStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //Check arg is valid + cmp dx, 0xC001; + jnz fail; + test eax, eax; + jl fail; + cmp eax, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge fail; + //set the new value + mov edx, ds:[eax*4 + 0x51C394]; + jmp end; +fail: + xor edx, edx; +end: + //Pass back the result + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + //Restore registers and return + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetPCExtraStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //Check arg is valid + cmp dx, 0xC001; + jnz fail; + test eax, eax; + jl fail; + cmp eax, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge fail; + //set the new value + mov edx, ds:[eax*4 + 0x51C420]; + jmp end; +fail: + xor edx, edx; +end: + //Pass back the result + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + //Restore registers and return + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetCritterBaseStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + push esi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //eax now contains the critter ID, esi the stat ID, and edi the new value + //Check args are valid + mov ebx, [esp]; + cmp bx, 0xC001; + jnz end; + mov ebx, [esp+4]; + cmp bx, 0xC001; + jnz end; + mov ebx, [esp+8]; + cmp bx, 0xC001; + jnz end; + test esi, esi; + jl end; + cmp esi, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge end; + //set the new value + mov edx, esp; + mov eax, [eax+0x64]; + mov ebx, 0x4A2108; + call ebx; + mov eax, [esp]; + add eax, 0x20; + mov ds:[eax + esi*4 + 4], edi; +end: + //Restore registers and return + add esp, 12; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetCritterExtraStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + push esi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //eax now contains the critter ID, esi the stat ID, and edi the new value + //Check args are valid + mov ebx, [esp]; + cmp bx, 0xC001; + jnz end; + mov ebx, [esp+4]; + cmp bx, 0xC001; + jnz end; + mov ebx, [esp+8]; + cmp bx, 0xC001; + jnz end; + test esi, esi; + jl end; + cmp esi, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge end; + //set the new value + mov edx, esp; + mov eax, [eax+0x64]; + mov ebx, 0x4A2108; + call ebx; + mov eax, [esp]; + add eax, 0x20; + mov ds:[eax + esi*4 + 0x90], edi; +end: + //Restore registers and return + add esp, 12; + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetCritterBaseStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + push eax + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //eax contains the critter pointer, and edi contains the stat id + //Check args are valid + mov ebx, [esp]; + cmp bx, 0xC001; + jnz fail; + mov ebx, [esp+4]; + cmp bx, 0xc001; + jnz fail; + test edi, edi; + jl fail; + cmp edi, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge fail; + //set the new value + mov edx, esp; + mov eax, [eax+0x64]; + mov ebx, 0x4A2108; + call ebx; + mov eax, [esp]; + add eax, 0x20; + mov edx, ds:[eax + edi*4 + 4]; + jmp end; +fail: + xor edx, edx; +end: + //Pass back the result + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + //Restore registers and return + add esp, 8; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetCritterExtraStat() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + //Get function args + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + push eax + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + push eax + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + //eax contains the critter pointer, and edi contains the stat id + //Check args are valid + mov ebx, [esp]; + cmp bx, 0xC001; + jnz fail; + mov ebx, [esp+4]; + cmp bx, 0xc001; + jnz fail; + test edi, edi; + jl fail; + cmp edi, 0x23; //23, 24 and 25 are valid, but stored elsewhere. Ignore for now. + jge fail; + //set the new value + mov edx, esp; + mov eax, [eax+0x64]; + mov ebx, 0x4A2108; + call ebx; + mov eax, [esp]; + add eax, 0x20; + mov edx, ds:[eax + edi*4 + 0x90]; + jmp end; +fail: + xor edx, edx; +end: + //Pass back the result + mov eax, ecx; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + //Restore registers and return + add esp, 8; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) set_critter_skill_points() { + __asm { + pushad; + //Get function args + mov ecx, eax; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + mov edi, eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + mov esi, eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + //eax now contains the critter ID, esi the skill ID, and edi the new value + //Check args are valid + mov ebx, [esp]; + cmp bx, 0xC001; + jnz end; + mov ebx, [esp+4]; + cmp bx, 0xC001; + jnz end; + mov ebx, [esp+8]; + cmp bx, 0xC001; + jnz end; + test esi, esi; + jl end; + cmp esi, 18; + jge end; + //set the new value + mov eax, [eax+0x64]; + mov edx, esp; + mov ebx, 0x4A2108; + call ebx; + mov eax, [esp]; + mov [eax+0x13c+esi*4], edi; +end: + //Restore registers and return + add esp, 12; + popad + retn; + } +} +static void __declspec(naked) get_critter_skill_points() { + __asm { + pushad; + //Get function args + mov ecx, eax; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + mov esi, eax; + mov eax, ecx; + call GetArgType; + push eax; + mov eax, ecx; + call GetArg; + //eax now contains the critter ID, esi the skill ID + //Check args are valid + mov ebx, [esp]; + cmp bx, 0xC001; + jnz fail; + mov ebx, [esp+4]; + cmp bx, 0xC001; + jnz fail; + test esi, esi; + jl fail; + cmp esi, 18; + jge fail; + //get the value + mov eax, [eax+0x64]; + mov edx, esp; + mov ebx, 0x4A2108; + call ebx; + mov eax, [esp]; + mov edx, [eax+0x13c+esi*4]; + jmp end; +fail: + xor edx, edx; +end: + mov eax, ecx; + call SetResult; + mov edx, 0xc001; + mov eax, ecx; + call SetResultType; + //Restore registers and return + add esp, 8; + popad + retn; + } +} +static void __declspec(naked) set_available_skill_points() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + xchg eax, ecx; + call GetArg; + cmp cx, 0xc001; + jnz end; + mov edx, eax; + xor eax, eax; + mov ecx, 0x4AF910; + call ecx; //_stat_pc_set +end: + popad; + retn; + } +} +static void __declspec(naked) get_available_skill_points() { + __asm { + pushad; + mov ecx, eax; + mov edx, dword ptr ds:[0x6681AC]; + call SetResult; + mov edx, 0xc001; + mov eax, ecx; + call SetResultType + popad; + retn; + } +} +static void __declspec(naked) mod_skill_points_per_level() { + __asm { + pushad; + mov ecx, eax; + call GetArgType; + xchg eax, ecx; + call GetArg; + cmp cx, 0xc001; + jnz end; + cmp eax, 100; + jg end; + cmp eax, -100; + jl end; + add eax, 5; + push eax; + push 0x43C27a; + call SafeWrite8; +end: + popad; + retn; + } +} +static void __declspec(naked) GetCritterAP() { + __asm { + //Store registers + push ecx; + push edx; + push edi; + //Get function args + mov ecx, eax; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + //Check args are valid + cmp di, 0xC001; + jnz fail; + mov edx, [eax+0x40]; + jmp end; +fail: + xor edx, edx; +end: + //Pass back the result + mov eax, ecx; + call SetResult; + mov edx, 0xc001; + mov eax, ecx; + call SetResultType; + //Restore registers and return + pop edi; + pop edx; + pop ecx; + retn; + } +} +#define COMBAT_FREE_MOVE_ADR 0x56D39C +#define DUDE_OBJ_ADR 0x6610B8 +static const DWORD intface_update_move_points_ = 0x45EE0C; +static void __declspec(naked) SetCritterAP() { + __asm { + //Store registers + push ebx; + push ecx; + push edx; + push edi; + push esi; + //Get function args + mov ecx, eax; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + mov ebx, eax; + mov eax, ecx; + call GetArgType; + mov esi, eax; + mov eax, ecx; + call GetArg; + //Check args are valid + cmp di, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + mov [eax+0x40], ebx; + mov ecx, ds:[DUDE_OBJ_ADR]; + cmp ecx, eax; + jne end; + mov eax, ebx; + mov edx, ds:[COMBAT_FREE_MOVE_ADR]; + call intface_update_move_points_; +end: + //Restore registers and return + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} + + +static void __declspec(naked) fSetPickpocketMax() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + and eax, 0xff; + cmp eax, 100; + jg end; + push 0; + push eax; + push 0xffffffff; + call SetPickpocketMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetHitChanceMax() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + and eax, 0xff; + cmp eax, 100; + jg end; + push 0; + push eax; + push 0xffffffff; + call SetHitChanceMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetCritterHitChance() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + xor ebx, ebx + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov edx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + test ebx, ebx; + jnz end; + push ecx; + push edx; + push eax; + call SetHitChanceMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetBaseHitChance() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + xor ebx, ebx + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + test ebx, ebx; + jnz end; + push ecx; + push eax; + push 0xffffffff; + call SetHitChanceMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetCritterPickpocket() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + xor ebx, ebx + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov edx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + test ebx, ebx; + jnz end; + push ecx; + push edx; + push eax; + call SetPickpocketMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetBasePickpocket() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + xor ebx, ebx + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + test ebx, ebx; + jnz end; + push ecx; + push eax; + push 0xffffffff; + call SetPickpocketMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetCritterSkillMod() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + xor ebx, ebx + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov ecx, eax; + mov eax, edi; + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + test ebx, ebx; + jnz end; + push ecx; + push eax; + call SetSkillMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetBaseSkillMod() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov edi, eax; + xor ebx, ebx + call GetArgType; + cmp ax, 0xc001; + cmovne ebx, edi; + mov eax, edi; + call GetArg; + mov ecx, eax; + test ebx, ebx; + jnz end; + push eax; + push 0xffffffff; + call SetSkillMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetSkillMax() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + and eax, 0xffff; + cmp eax, 300; + jg end; + push eax; + push 0xffffffff; + call SetSkillMax; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetStatMax() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + push edi; + push eax; + push edi; + push eax; + call SetPCStatMax; + call SetNPCStatMax; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetStatMin() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + push edi; + push eax; + push edi; + push eax; + call SetPCStatMax; + call SetNPCStatMax; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetPCStatMax() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + push edi; + push eax; + call SetPCStatMax; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetPCStatMin() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + push edi; + push eax; + call SetPCStatMax; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetNPCStatMax() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + push edi; + push eax; + call SetNPCStatMax; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) fSetNPCStatMin() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + push edi; + push eax; + call SetNPCStatMax; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static float xpmod; +static DWORD xptmp; +static void __declspec(naked) SetXpMod3() { + __asm { + push ebx; + push ecx; + push esi; + push edi; + push ebp; + mov xptmp, eax; + fild xptmp; + fmul xpmod; + fistp xptmp; + mov eax, xptmp; + mov ebx, 0x004AFABD; + jmp ebx; + } +} +static void _stdcall SetXpMod2(DWORD percent) { + SafeWrite8(0x004AFAB8, 0xe9); + SafeWrite32(0x004AFAB9, (DWORD)&SetXpMod3 - 0x004AFABD); + xpmod=(float)percent/100.0f; +} +static void __declspec(naked) SetXpMod() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + and eax, 0xffff; + push eax; + call SetXpMod2; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static int PerkLevelMod; +static void __declspec(naked) SetPerkLevelMod3() { + __asm { + push ebx; + mov ebx, 0x004AF8FC; + call ebx; + pop ebx; + add eax, PerkLevelMod; + cmp eax, 0; + jge end; + xor eax, eax; +end: + retn; + } +} +static void _stdcall SetPerkLevelMod2(int mod) { + if(mod<-25||mod>25) return; + PerkLevelMod=mod; + SafeWrite32(0x00496880, (DWORD)&SetPerkLevelMod3 - 0x00496884); +} +static void __declspec(naked) SetPerkLevelMod() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push eax; + call SetPerkLevelMod2; +end: + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/WorldmapOps.hpp b/sfall/ScriptOps/WorldmapOps.hpp new file mode 100644 index 00000000..2ea34eab --- /dev/null +++ b/sfall/ScriptOps/WorldmapOps.hpp @@ -0,0 +1,221 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "main.h" +#include "ScriptExtender.h" + + +static DWORD EncounteredHorrigan; +static void _stdcall ForceEncounter4() { + *(DWORD*)0x00672E04=EncounteredHorrigan; + SafeWrite32(0x004C070E, 0x95); + SafeWrite32(0x004C0718, 0x95); + SafeWrite32(0x004C06D1, 0x2E043D83); + SafeWrite32(0x004C071D, 0xFFFC2413); + SafeWrite8(0x4C0706, 0x75); +} +static void __declspec(naked) ForceEncounter3() { + __asm { + push eax; + push ebx; + push ecx; + push edx; + mov eax, [esp+0x10]; + sub eax, 5; + mov [esp+0x10], eax; + call ForceEncounter4; + pop edx; + pop ecx; + pop ebx; + pop eax; + retn; + } +} +static void _stdcall ForceEncounter2(DWORD mapID, DWORD flags) { + EncounteredHorrigan=*(DWORD*)0x00672E04; + SafeWrite32(0x004C070E, mapID); + SafeWrite32(0x004C0718, mapID); + SafeWrite32(0x004C06D1, 0x18EBD231); //xor edx, edx / jmp 0x18 + SafeWrite32(0x004C071D, ((DWORD)&ForceEncounter3) - 0x004C0721); + if(flags&1) SafeWrite8(0x4C0706, 0xeb); +} +static void __declspec(naked) ForceEncounter() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + push 0; + push eax; + call ForceEncounter2; +end: + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) ForceEncounterWithFlags() { + __asm { + pushad + mov ecx, eax; + call GetArgType; + mov edx, eax; + mov eax, ecx; + call GetArg; + mov ebx, eax; + mov eax, ecx; + call GetArgType; + mov edi, eax; + mov eax, ecx; + call GetArg; + cmp dx, 0xC001; + jnz end; + cmp di, 0xc001; + jnz end; + push ebx; + push eax; + call ForceEncounter2; +end: + popad + retn; + } +} + +// world_map_functions +static void __declspec(naked) funcInWorldMap() { + __asm { + push ebx; + push ecx; + push edx; + push esi; + mov esi, eax; + call InWorldMap; + mov edx, eax; + mov eax, esi; + call SetResult; + mov edx, 0xc001; + mov eax, esi; + call SetResultType; + pop esi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetGameMode() { + __asm { + pushad; + mov edi, eax; + call GetCurrentLoops; + mov edx, eax; + mov eax, edi; + call SetResult; + mov edx, 0xc001; + mov eax, edi; + call SetResultType; + popad; + retn; + } +} +static void __declspec(naked) GetWorldMapXPos() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x00672E0C]; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) GetWorldMapYPos() { + __asm { + push ebx; + push ecx; + push edx; + mov ecx, eax; + mov edx, ds:[0x00672E10]; + mov ebx, 0x4674DC; + call ebx; + mov edx, 0xc001; + mov eax, ecx; + mov ebx, 0x46748C; + call ebx; + pop edx; + pop ecx; + pop ebx; + retn; + } +} +static void __declspec(naked) SetWorldMapPos() { + __asm { + push ebx; + push ecx; + push edx; + push edi; + push esi; + mov ecx, eax; + mov ebx, 0x4674F0; + call ebx; + mov esi, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + mov edi, eax; + mov eax, ecx; + mov ebx, 0x4674F0; + call ebx; + mov edx, eax; + mov eax, ecx; + mov ebx, 0x467500; + call ebx; + cmp dx, 0xC001; + jnz end; + cmp si, 0xC001; + jnz end; + mov ds:[0x00672E0C], eax; + mov ds:[0x00672E10], edi; +end: + pop esi; + pop edi; + pop edx; + pop ecx; + pop ebx; + retn; + } +} \ No newline at end of file diff --git a/sfall/ScriptOps/readme.txt b/sfall/ScriptOps/readme.txt new file mode 100644 index 00000000..2808026e --- /dev/null +++ b/sfall/ScriptOps/readme.txt @@ -0,0 +1 @@ +Code for all new scripting functions should go here (ASM code). \ No newline at end of file diff --git a/sfall/Stats.cpp b/sfall/Stats.cpp new file mode 100644 index 00000000..aa660317 --- /dev/null +++ b/sfall/Stats.cpp @@ -0,0 +1,305 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Stats.h" +#include +#include + +static DWORD StatMaximumsPC[0x23]; +static DWORD StatMinimumsPC[0x23]; +static DWORD StatMaximumsNPC[0x23]; +static DWORD StatMinimumsNPC[0x23]; + +static DWORD cCritter; + +static DWORD xpTable[99]; + +static void __declspec(naked) GetCurrentStatHook1() { + __asm { + mov cCritter, eax; + push ebx; + push ecx; + push esi; + push edi; + push ebp; + mov ebx, 0x004AEF4D; + jmp ebx; + } +} +static void __declspec(naked) GetCurrentStatHook2() { + __asm { + shl esi, 2; + mov eax, cCritter; + cmp eax, dword ptr ds:[0x006610B8]; + je pc; + cmp ecx, StatMinimumsNPC[esi]; + jg npc1; + mov eax, StatMinimumsNPC[esi]; + jmp end; +npc1: + cmp ecx, StatMaximumsNPC[esi]; + jl npc2; + mov eax, StatMaximumsNPC[esi]; + jmp end; +npc2: + mov eax, ecx; + jmp end; +pc: + cmp ecx, StatMinimumsPC[esi]; + jge pc1; + mov eax, StatMinimumsPC[esi]; + jmp end; +pc1: + cmp ecx, StatMaximumsPC[esi]; + jle pc2; + mov eax, StatMaximumsPC[esi]; + jmp end; +pc2: + mov eax, ecx; +end: + mov ebx, 0x004AF3D7; + jmp ebx; + } +} + +static void __declspec(naked) SetCurrentStatHook() { + __asm { + cmp esi, dword ptr ds:[0x006610B8]; + je pc; + cmp ebx, StatMinimumsNPC[ecx*4]; + jl fail; + cmp ebx, StatMaximumsNPC[ecx*4]; + jg fail; + jmp end; +pc: + cmp ebx, StatMinimumsPC[ecx*4]; + jl fail; + cmp ebx, StatMaximumsPC[ecx*4]; + jg fail; + jmp end; +fail: + mov eax, 0x004AF57E; + jmp eax; +end: + mov edx, 0x004AF59C; + jmp edx; + } +} + +static void __declspec(naked) GetLevelXPHook() { + __asm { + dec eax; + mov eax, [xpTable+eax*4]; + ret; + } +} +static void __declspec(naked) GetNextLevelXPHook() { + __asm { + mov eax, ds:[0x6681B0]; + jmp GetLevelXPHook; + } +} + +unsigned short StandardApAcBonus=4; +unsigned short ExtraApAcBonus=4; +static const DWORD ApAcRetAddr=0x4AF0A4; +static void __declspec(naked) ApplyApAcBonus() { + __asm { + push edi; + push edx; + cmp [esp+12], 2; + jge h2hEvade; + xor edi, edi; + jmp standard; +h2hEvade: + mov edx, 93; + mov eax, dword ptr ds:[0x6610B8]; + mov edi, 0x496B78; + call edi; + imul ax, ExtraApAcBonus; + imul ax, [ebx+0x40]; + mov edi, eax; +standard: + mov eax, [ebx+0x40]; + imul ax, StandardApAcBonus; + add eax, edi; + shr eax, 2; + pop edx; + pop edi; + jmp ApAcRetAddr; + } +} + +static const DWORD stat_level=0x4AEF48; +static const DWORD proto_ptr=0x4A2108; +static int StatFormulas[33*2]; +static int StatShifts[33*7]; +static double StatMulti[33*7]; +static int __declspec(naked) _stdcall StatLevel(void* critter, int id) { + __asm { + mov eax, [esp+4]; + mov edx, [esp+8]; + call stat_level; + retn 8; + } +} +static void __declspec(naked) _stdcall ProtoPtr(DWORD pid, int** proto) { + __asm { + mov eax, [esp+4]; + mov edx, [esp+8]; + call proto_ptr; + retn 8; + } +} +static void _stdcall StatRecalcDerived(DWORD* critter) { + int basestats[7]; + for(int i=0;i<7;i++) basestats[i]=StatLevel(critter, i); + int* proto; + ProtoPtr(critter[25], &proto); + + for(int i=7;i<=32;i++) { + if(i>=17&&i<=30) continue; + + double sum=0; + for(int j=0;j<7;j++) { + sum+=(basestats[j]+StatShifts[i*7+j])*StatMulti[i*7+j]; + } + proto[i+9]=StatFormulas[i*2] + (int)floor(sum); + if(proto[i+9]0) { + char *ptr=table, *ptr2; + DWORD level=0; + + HookCall(0x434AA7, GetNextLevelXPHook); + HookCall(0x439642, GetNextLevelXPHook); + HookCall(0x4AFB22, GetNextLevelXPHook); + HookCall(0x496C8D, GetLevelXPHook); + HookCall(0x4AFC53, GetLevelXPHook); + + while((ptr2=strstr(ptr, ","))&&level<99) { + ptr2[0]='\0'; + xpTable[level++]=atoi(ptr); + ptr=ptr2+1; + } + if(level<99&&ptr[0]!='\0') { + xpTable[level++]=atoi(ptr); + } + for(int i=level;i<99;i++) xpTable[i]=-1; + SafeWrite8(0x4AFB1B, (BYTE)(level+1)); + } + + GetPrivateProfileStringA("Misc", "DerivedStats", "", table, 2048, ini); + if(strlen(table)) { + MakeCall(0x4AF6FC, &stat_recalc_derived, true); + memset(StatFormulas, 0, sizeof(StatFormulas)); + memset(StatShifts, 0, sizeof(StatShifts)); + memset(StatMulti, 0, sizeof(StatMulti)); + + StatFormulas[7*2]=15; //max hp + StatMulti[7*7+0]=1; + StatMulti[7*7+2]=2; + StatFormulas[8*2]=5; //max ap + StatMulti[8*7+5]=0.5; + StatMulti[9*7+5]=1; //ac + StatFormulas[11*2+1]=1; //melee damage + StatShifts[11*7+0]=-5; + StatMulti[11*7+0]=1; + StatFormulas[12*2]=25; //carry weight + StatMulti[12*7+0]=25; + StatMulti[13*7+1]=2; //sequence + StatFormulas[14*2+1]=1; //heal rate + StatMulti[14*7+2]=1.0/3.0; + StatMulti[15*7+6]=1; //critical chance + StatMulti[31*7+2]=2; //rad resist + StatMulti[32*7+2]=5; //poison resist + + char key[6], buf2[256], buf3[256]; + strcpy(buf3, table); + sprintf(table, ".\\%s", buf3); + for(int i=7;i<=32;i++) { + if(i>=17&&i<=30) continue; + + _itoa(i, key, 10); + StatFormulas[i*2]=GetPrivateProfileInt(key, "base", StatFormulas[i*2], table); + StatFormulas[i*2+1]=GetPrivateProfileInt(key, "min", StatFormulas[i*2+1], table); + for(int j=0;j<7;j++) { + sprintf(buf2, "shift%d", j); + StatShifts[i*7+j]=GetPrivateProfileInt(key, buf2, StatShifts[i*7+0], table); + sprintf(buf2, "multi%d", j); + _gcvt(StatMulti[i*7+j], 16, buf3); + GetPrivateProfileStringA(key, buf2, buf3, buf2, 256, table); + StatMulti[i*7+j]=atof(buf2); + } + } + } +} + +void _stdcall SetPCStatMax(int stat, int i) { + if(stat<0||stat>=0x23) return; + StatMaximumsPC[stat]=i; +} +void _stdcall SetPCStatMin(int stat, int i) { + if(stat<0||stat>=0x23) return; + StatMinimumsPC[stat]=i; +} +void _stdcall SetNPCStatMax(int stat, int i) { + if(stat<0||stat>=0x23) return; + StatMaximumsNPC[stat]=i; +} +void _stdcall SetNPCStatMin(int stat, int i) { + if(stat<0||stat>=0x23) return; + StatMinimumsNPC[stat]=i; +} \ No newline at end of file diff --git a/sfall/Stats.h b/sfall/Stats.h new file mode 100644 index 00000000..8b8b0004 --- /dev/null +++ b/sfall/Stats.h @@ -0,0 +1,28 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void StatsReset(); +void StatsInit(); +void _stdcall SetPCStatMax(int stat, int i); +void _stdcall SetPCStatMin(int stat, int i); +void _stdcall SetNPCStatMax(int stat, int i); +void _stdcall SetNPCStatMin(int stat, int i); +extern unsigned short StandardApAcBonus; +extern unsigned short ExtraApAcBonus; \ No newline at end of file diff --git a/sfall/SuperSave.cpp b/sfall/SuperSave.cpp new file mode 100644 index 00000000..71158e5c --- /dev/null +++ b/sfall/SuperSave.cpp @@ -0,0 +1,595 @@ +/* + * sfall + * Copyright (C) 2009, 2010 Mash (Matt Wells, mashw at bigpond dot net dot au) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include "SuperSave.h" +#include "HeroAppearance.h" + +//extern +DWORD LSPageOffset=0; + + +int LSButtDN=0; + + +//-------------------------------------- +void SavePageOffsets() { + + char SavePath[MAX_PATH]; + + char buffer[6]; + + strcpy_s(SavePath, MAX_PATH, *(char**)0x5193CC); + strcat_s(SavePath, MAX_PATH, "savegame\\SLOTDAT.ini"); + + _itoa_s(*(DWORD*)0x5193B8, buffer, 10); + WritePrivateProfileString("POSITION", "ListNum", buffer, SavePath); + _itoa_s(LSPageOffset, buffer, 10); + WritePrivateProfileString("POSITION", "PageOffset", buffer, SavePath); + +} + + +//------------------------------------------ +static void __declspec(naked) save_page_offsets(void) { + + __asm { + //save last slot position values to file + call SavePageOffsets + //restore original code + mov eax, dword ptr ds:[0x6142C4] + ret + } + +} + + +//-------------------------------------- +void LoadPageOffsets() { + + char LoadPath[MAX_PATH]; + + strcpy_s(LoadPath, MAX_PATH, *(char**)0x5193CC); + strcat_s(LoadPath, MAX_PATH, "savegame\\SLOTDAT.ini"); + + *(DWORD*)0x5193B8=GetPrivateProfileInt("POSITION", "ListNum", 0, LoadPath); + if(*(DWORD*)0x5193B8>9)*(DWORD*)0x5193B8=9; + + LSPageOffset=GetPrivateProfileInt("POSITION", "PageOffset", 0, LoadPath); + if(LSPageOffset>9990)LSPageOffset=9990; + +} + + +//------------------------------------------ +static void __declspec(naked) load_page_offsets(void) { + + + __asm { + //load last slot position values from file + call LoadPageOffsets + //restore original code + mov edx, 0x50A480 // ASCII "SAV" + ret + } + +} + + +//------------------------------------------ +static void __declspec(naked) create_page_buttons(void) { + + __asm { + //left button -10 + push 32//ButType + push 0//? always 0 + push 0//PicDown + push 0//PicUp + push 0x14B//ButtUp left button + push 0x54B//ButtDown + push 0x500//HovOff + push -1//HovOn + push 20//Height + mov ecx, 24//Width + mov edx, 100//Xpos + mov ebx, 56//Ypos + mov eax, dword ptr ds:[0x6142C4]//WinRef + mov esi, 0x4D8260 + call esi + //left button -100 + push 32//ButType + push 0//? always 0 + push 0//PicDown + push 0//PicUp + push 0x149//ButtUp PGUP button + push 0x549//ButtDown + push 0x500//HovOff + push -1//HovOn + push 20//Height + mov ecx, 24//Width + mov edx, 68//Xpos + mov ebx, 56//Ypos + mov eax, dword ptr ds:[0x6142C4]//WinRef + mov esi, 0x4D8260//create button function + call esi + //right button +10 + push 32//ButType + push 0//? always 0 + push 0//PicDown + push 0//PicUp + push 0x14D//ButtUp right button + push 0x54D//ButtDown + push 0x500//HovOff + push -1//HovOn + push 20//Height + mov ecx, 24//Width + mov edx, 216//Xpos + mov ebx, 56//Ypos + mov eax, dword ptr ds:[0x6142C4]//WinRef + mov esi, 0x4D8260//create button function + call esi + //right button +100 + push 32//ButType + push 0//? always 0 + push 0//PicDown + push 0//PicUp + push 0x151//ButtUp PGDN button + push 0x551//ButtDown + push 0x500//HovOff + push -1//HovOn + push 20//Height + mov ecx, 24//Width + mov edx, 248//Xpos + mov ebx, 56//Ypos + mov eax, dword ptr ds:[0x6142C4]//WinRef + mov esi, 0x4D8260//create button function + call esi + //Set Number button + push 32//ButType + push 0//? always 0 + push 0//PicDown + push 0//PicUp + push -1//ButtUp + push 'p'//ButtDown + push -1//HovOff + push -1//HovOn + push 20//Height + mov ecx, 60//Width + mov edx, 140//Xpos + mov ebx, 56//Ypos + mov eax, dword ptr ds:[0x6142C4]//WinRef + mov esi, 0x4D8260//create button function + call esi + + //restore original code + mov eax, 0x65 + ret + } +} + + +//------------------------------------------------------ +void SetPageNum() { + + int WinRef=*(DWORD*)0x6142C4;//load/save winref + if(WinRef==NULL)return; + WINinfo *SaveLoadWin = GetWinStruct(WinRef); + if(SaveLoadWin->surface==NULL)return; + + BYTE ConsoleGold = *(BYTE*)0x6AB8BB;//palette offset stored in mem - text colour + + char TempText[32]; + unsigned int TxtMaxWidth=GetMaxCharWidth()*8;//GetTextWidth(TempText); + unsigned int TxtWidth=0; + + DWORD NewTick=0, OldTick=0; + int button=0; + int exitFlag=0; + char blip='_'; + char Number[5]; + int numpos=0; + + DWORD tempPageOffset=-1; + + + while(!exitFlag) { + NewTick = GetTickCount();//timer for redraw + if(OldTick>NewTick) + OldTick=NewTick; + if(NewTick-OldTick>166) {//time to draw + OldTick=NewTick; + + if(blip=='_')blip=' '; + else blip='_'; + + + sprintf_s(TempText, 32, "#%d%c", tempPageOffset/10+1, '_'); + if(tempPageOffset==-1) + sprintf_s(TempText, 32, "#%c", '_'); + TxtWidth=GetTextWidth(TempText); + + sprintf_s(TempText, 32, "#%d%c", tempPageOffset/10+1, blip); + if(tempPageOffset==-1) + sprintf_s(TempText, 32, "#%c", blip); + + //fill over text area with consol black colour + for(unsigned int y=SaveLoadWin->width*52; ywidth*82; y=y+SaveLoadWin->width) + memset(SaveLoadWin->surface+y+170-TxtMaxWidth/2, 0xCF, TxtMaxWidth); + + + PrintText(TempText, ConsoleGold, 170-TxtWidth/2, 60, TxtWidth, SaveLoadWin->width, SaveLoadWin->surface); + RedrawWin(WinRef); + } + + button=check_buttons(); + if(button>='0' && button<='9') { + if(numpos<4) { + Number[numpos]=button; + Number[numpos+1]='\0'; + numpos++; + if(Number[0]=='0') { + numpos=0; + tempPageOffset=-1; + } + else + tempPageOffset=(atoi(Number)-1)*10; + } + //else exitFlag=-1; + } + else if(button==0x08 && numpos) { + numpos--; + Number[numpos]='\0'; + if(!numpos) tempPageOffset=-1; + else tempPageOffset=(atoi(Number)-1)*10; + } + else if(button==0x0D || button==0x20 || button=='p' || button=='P') exitFlag=-1;//Enter, Space or P Keys + else if(button==0x1B)tempPageOffset=-1, exitFlag=-1;//Esc key + } + + if(tempPageOffset!=-1 && tempPageOffset<=9990) + LSPageOffset=tempPageOffset; + + SaveLoadWin=NULL; +} + + +//------------------------------------------ +static void __declspec(naked) check_page_buttons(void) { +/* +0047BD49 |> 3D 48010000 |CMP EAX,148 +0047BD4E |. 75 2E |JNZ SHORT fallout2.0047BD7E +0047BD50 |. 8B15 B8935100 |MOV EDX,DWORD PTR DS:[5193B8] +0047BD56 |. 4A |DEC EDX +0047BD57 |. 8915 B8935100 |MOV DWORD PTR DS:[5193B8],EDX +0047BD5D |. 85D2 |TEST EDX,EDX +0047BD5F |. 7D 07 |JGE SHORT fallout2.0047BD68 +0047BD61 |. 31C0 |XOR EAX,EAX +0047BD63 |. A3 B8935100 |MOV DWORD PTR DS:[5193B8],EAX +0047BD68 |> B9 FFFFFFFF |MOV ECX,-1 //button pressed exit check +0047BD6D |. BA 01000000 |MOV EDX,1 +0047BD72 |. 898C24 28020000 |MOV DWORD PTR SS:[ESP+228],ECX +0047BD79 |. E9 7B010000 |JMP fallout2.0047BEF9 +*/ + + __asm { + cmp eax, 0x14B//left button + jnz CheckFastLeft + cmp LSPageOffset, 10 + jl SetRet + sub LSPageOffset, 10 + jmp SetRet + CheckFastLeft: + cmp eax, 0x149//fast left PGUP button + jnz CheckRight + cmp LSPageOffset, 100 + jl FirstPage + sub LSPageOffset, 100 + jmp SetRet + FirstPage: + mov LSPageOffset, 0 + jmp SetRet + CheckRight: + cmp eax, 0x14D//right button + jnz CheckFastRight + cmp LSPageOffset, 9980 + jg SetRet + add LSPageOffset, 10 + jmp SetRet + CheckFastRight: + cmp eax, 0x151//fast right PGDN button + jnz CheckSetNumber + cmp LSPageOffset, 9890 + jg LastPage + add LSPageOffset, 100 + jmp SetRet +LastPage: + mov LSPageOffset, 9990 + jmp SetRet + CheckSetNumber: + cmp eax, 'p'//p button pressed - start SetPageNum func + jnz CheckSetNumber2 + pushad + call SetPageNum + popad + jmp SetRet + CheckSetNumber2: + cmp eax, 'P'//P button pressed - start SetPageNum func + jnz CheckButtonDown + pushad + call SetPageNum + popad + jmp SetRet + CheckButtonDown: + cmp eax, 0x500//button in down state + jl CheckUp + SetRet: + mov LSButtDN, eax + push esi + mov esi, 0x47E5D0 //reset page save list func + call esi + pop esi + add dword ptr ds:[esp], 26// set return to button pressed code + jmp EndFunc + CheckUp: + //restore original code + cmp eax, 0x148//up button + EndFunc: + ret + } +} + + +//------------------------------------------ +void DrawPageText() { + + int WinRef=*(DWORD*)0x6142C4;//load/save winref + if(WinRef==NULL)return; + WINinfo *SaveLoadWin = GetWinStruct(WinRef); + if(SaveLoadWin->surface==NULL)return; + + //fill over text area with consol black colour + for(unsigned int y=SaveLoadWin->width*52; ywidth*82; y=y+SaveLoadWin->width) + memset(SaveLoadWin->surface+50+y, 0xCF, 240); + + + BYTE ConsoleGreen = *(BYTE*)0x6A3CB0;//palette offset stored in mem - text colour + BYTE ConsoleGold = *(BYTE*)0x6AB8BB;//palette offset stored in mem - text colour + BYTE Colour = ConsoleGreen; + + char TempText[32]; + sprintf_s(TempText, 32, "[ %d ]", LSPageOffset/10+1); + + unsigned int TxtWidth=GetTextWidth(TempText); + PrintText(TempText, Colour, 170-TxtWidth/2, 60, TxtWidth, SaveLoadWin->width, SaveLoadWin->surface); + + if(LSButtDN==0x549) Colour=ConsoleGold; + else Colour = ConsoleGreen; + strcpy_s(TempText, 12, "<<"); + TxtWidth=GetTextWidth(TempText); + PrintText(TempText, Colour, 80-TxtWidth/2, 60, TxtWidth, SaveLoadWin->width, SaveLoadWin->surface); + + if(LSButtDN==0x54B) Colour=ConsoleGold; + else Colour = ConsoleGreen; + strcpy_s(TempText, 12, "<"); + TxtWidth=GetTextWidth(TempText); + PrintText(TempText, Colour, 112-TxtWidth/2, 60, TxtWidth, SaveLoadWin->width, SaveLoadWin->surface); + + + + if(LSButtDN==0x551) Colour=ConsoleGold; + else Colour = ConsoleGreen; + strcpy_s(TempText, 12, ">>"); + TxtWidth=GetTextWidth(TempText); + PrintText(TempText, Colour, 260-TxtWidth/2, 60, TxtWidth, SaveLoadWin->width, SaveLoadWin->surface); + + if(LSButtDN==0x54D) Colour=ConsoleGold; + else Colour = ConsoleGreen; + strcpy_s(TempText, 12, ">"); + TxtWidth=GetTextWidth(TempText); + PrintText(TempText, Colour, 228-TxtWidth/2, 60, TxtWidth, SaveLoadWin->width, SaveLoadWin->surface); + + SaveLoadWin=NULL; +} + + +//------------------------------------------ +static void __declspec(naked) draw_page_text(void) { + + __asm { + pushad + call DrawPageText + popad + //restore original code + mov ebp, 0x57 + ret + } +} + + +//------------------------------------------ +//add page num offset when reading and writing various save data files +static void __declspec(naked) AddPageOffset01(void) { + + __asm { + mov eax, dword ptr ds:[0x5193B8]//list position 0-9 + add eax, LSPageOffset//add page num offset + ret + } +} + + +//------------------------------------------ +//getting info for the 10 currently displayed save slots from save.dats +static void __declspec(naked) AddPageOffset02(void) { + + __asm { + pop edx//pop ret addr + mov eax, 0x50A514 //ASCII "SAVE.DAT" + push eax + lea eax, dword ptr ds:[ebx+1] + add eax, LSPageOffset//add page num offset + push edx//push ret addr + ret + } +} + + +//------------------------------------------ +//printing current 10 slot numbers +static void __declspec(naked) AddPageOffset03(void) { + + __asm { + inc eax + add eax, LSPageOffset//add page num offset + mov bl, byte ptr ss:[esp+0x10]//add 4 bytes - func ret addr + ret + } +} + + + +//-------------------------------------------------------------------------- +void EnableSuperSaving() { + + //save/load button setup func + SafeWrite8(0x47D80D, 0xE8); + SafeWrite32(0x47D80E, (DWORD)&create_page_buttons - 0x47D812); + + //Draw button text + SafeWrite8(0x47E6E8, 0xE8); + SafeWrite32(0x47E6E9, (DWORD)&draw_page_text - 0x47E6ED); + + //check save buttons + SafeWrite8(0x47BD49, 0xE8); + SafeWrite32(0x47BD4A, (DWORD)&check_page_buttons - 0x47BD4E); + + //check load buttons + SafeWrite8(0x47CB1C, 0xE8); + SafeWrite32(0x47CB1D, (DWORD)&check_page_buttons - 0x47CB21); + + + //save current page and list positions to file on load/save scrn exit + SafeWrite8(0x47D828, 0xE8); + SafeWrite32(0x47D829, (DWORD)&save_page_offsets - 0x47D82D); + + //load saved page and list positions from file + SafeWrite8(0x47B82B, 0xE8); + SafeWrite32(0x47B82C, (DWORD)&load_page_offsets - 0x47B830); + + + //Add Load/Save page offset to Load/Save folder number///////////////// + SafeWrite8(0x47B929, 0xE8); + SafeWrite32(0x47B92A, (DWORD)&AddPageOffset01 - 0x47B92E); + + SafeWrite8(0x47D8DB, 0xE8); + SafeWrite32(0x47D8DC, (DWORD)&AddPageOffset01 - 0x47D8E0); + + SafeWrite8(0x47D9B0, 0xE8); + SafeWrite32(0x47D9B1, (DWORD)&AddPageOffset01 - 0x47D9B5); + + SafeWrite8(0x47DA34, 0xE8); + SafeWrite32(0x47DA35, (DWORD)&AddPageOffset01 - 0x47DA39); + + SafeWrite8(0x47DABF, 0xE8); + SafeWrite32(0x47DAC0, (DWORD)&AddPageOffset01 - 0x47DAC4); + + SafeWrite8(0x47DB58, 0xE8); + SafeWrite32(0x47DB59, (DWORD)&AddPageOffset01 - 0x47DB5D); + + SafeWrite8(0x47DBE9, 0xE8); + SafeWrite32(0x47DBEA, (DWORD)&AddPageOffset01 - 0x47DBEE); + + SafeWrite8(0x47DC9C, 0xE8); + SafeWrite32(0x47DC9D, (DWORD)&AddPageOffset01 - 0x47DCA1); + + SafeWrite8(0x47EC77, 0xE8); + SafeWrite32(0x47EC78, (DWORD)&AddPageOffset01 - 0x47EC7C); + + SafeWrite8(0x47F5AB, 0xE8); + SafeWrite32(0x47F5AC, (DWORD)&AddPageOffset01 - 0x47F5B0); + + SafeWrite8(0x47F694, 0xE8); + SafeWrite32(0x47F695, (DWORD)&AddPageOffset01 - 0x47F699); + + SafeWrite8(0x47F6EB, 0xE8); + SafeWrite32(0x47F6EC, (DWORD)&AddPageOffset01 - 0x47F6F0); + + SafeWrite8(0x47F7FB, 0xE8); + SafeWrite32(0x47F7FC, (DWORD)&AddPageOffset01 - 0x47F800); + + SafeWrite8(0x47F892, 0xE8); + SafeWrite32(0x47F893, (DWORD)&AddPageOffset01 - 0x47F897); + + SafeWrite8(0x47FB86, 0xE8); + SafeWrite32(0x47FB87, (DWORD)&AddPageOffset01 - 0x47FB8B); + + SafeWrite8(0x47FC3A, 0xE8); + SafeWrite32(0x47FC3B, (DWORD)&AddPageOffset01 - 0x47FC3F); + + SafeWrite8(0x47FCF2, 0xE8); + SafeWrite32(0x47FCF3, (DWORD)&AddPageOffset01 - 0x47FCF7); + + SafeWrite8(0x480117, 0xE8); + SafeWrite32(0x480118, (DWORD)&AddPageOffset01 - 0x48011C); + + SafeWrite8(0x4801CF, 0xE8); + SafeWrite32(0x4801D0, (DWORD)&AddPageOffset01 - 0x4801D4); + + SafeWrite8(0x480234, 0xE8); + SafeWrite32(0x480235, (DWORD)&AddPageOffset01 - 0x480239); + + SafeWrite8(0x480310, 0xE8); + SafeWrite32(0x480311, (DWORD)&AddPageOffset01 - 0x480315); + + SafeWrite8(0x4803F3, 0xE8); + SafeWrite32(0x4803F4, (DWORD)&AddPageOffset01 - 0x4803F8); + + SafeWrite8(0x48049F, 0xE8); + SafeWrite32(0x4804A0, (DWORD)&AddPageOffset01 - 0x4804A4); + + SafeWrite8(0x480512, 0xE8); + SafeWrite32(0x480513, (DWORD)&AddPageOffset01 - 0x480517); + + SafeWrite8(0x4805F2, 0xE8); + SafeWrite32(0x4805F3, (DWORD)&AddPageOffset01 - 0x4805F7); + + SafeWrite8(0x480767, 0xE8); + SafeWrite32(0x480768, (DWORD)&AddPageOffset01 - 0x48076C); + + SafeWrite8(0x4807E6, 0xE8); + SafeWrite32(0x4807E7, (DWORD)&AddPageOffset01 - 0x4807EB); + + SafeWrite8(0x480839, 0xE8); + SafeWrite32(0x48083A, (DWORD)&AddPageOffset01 - 0x48083E); + + SafeWrite8(0x4808D3, 0xE8); + SafeWrite32(0x4808D4, (DWORD)&AddPageOffset01 - 0x4808D8); + + + SafeWrite8(0x47E5E1, 0xE8); + SafeWrite32(0x47E5E2, (DWORD)&AddPageOffset02 - 0x47E5E6); + SafeWrite16(0x47E5E6, 0x9090); + SafeWrite8(0x47E5E8, 0x90); + + + SafeWrite8(0x47E756, 0xE8); + SafeWrite32(0x47E757, (DWORD)&AddPageOffset03 - 0x47E75B); + +} diff --git a/sfall/SuperSave.h b/sfall/SuperSave.h new file mode 100644 index 00000000..8c78d003 --- /dev/null +++ b/sfall/SuperSave.h @@ -0,0 +1,27 @@ +/* + * sfall + * Copyright (C) 2009 Mash (Matt Wells, mashw at bigpond dot net dot au) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + + + +extern DWORD LSPageOffset; + +void EnableSuperSaving(); + + diff --git a/sfall/Tiles.cpp b/sfall/Tiles.cpp new file mode 100644 index 00000000..7b8688f4 --- /dev/null +++ b/sfall/Tiles.cpp @@ -0,0 +1,317 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Tiles.h" +#include +#include "FileSystem.h" +#include + +struct sArt { + DWORD flags; + char path[16]; + char* names; + int d18; + int total; + + sArt(char* str) { + flags=0; + strncpy_s(path, str, 16); + names=0; + d18=0; + total=0; + } +}; +struct OverrideEntry { + //DWORD id; + DWORD xtiles; + DWORD ytiles; + DWORD replacementid; + + OverrideEntry(DWORD _xtiles, DWORD _ytiles, DWORD _repid) { + xtiles=_xtiles; + ytiles=_ytiles; + replacementid=_repid; + } +}; +#pragma pack(push, 1) +struct frm { + DWORD _id; //0x00 + DWORD unused; //0x04 + WORD frames; //0x08 + WORD xshift[6]; //0x0a + WORD yshift[6]; //0x16 + DWORD framestart[6];//0x22 + DWORD size; //0x3a + WORD width; //0x3e + WORD height; //0x40 + DWORD frmsize; //0x42 + WORD xoffset; //0x46 + WORD yoffset; //0x48 + BYTE pixels[80*36]; //0x4a +}; +#pragma pack(pop) + +static OverrideEntry** overrides; +static DWORD origTileCount=0; + +static const DWORD _db_fopen=0x4C5EC8; +static const DWORD _db_fgets=0x4C5F70; +static const DWORD _db_fclose=0x4C5EB4; +static const DWORD _db_freadByteCount=0x4C62FC; +static const DWORD _db_freadShort=0x4C60F4; +static const DWORD _db_freadInt=0x4C614C; +static const DWORD _db_fseek=0x4C60C0; +static const DWORD _db_fwriteByteCount=0x4C6464; +static const DWORD _mem_realloc=0x4C5B50; +static const DWORD _db_freadIntCount=0x4C63BC; + +static DWORD db_fopen(const char* path, const char* mode) { + DWORD result; + __asm { + mov eax, path; + mov edx, mode; + call _db_fopen; + mov result, eax; + } + return result; +} +static char* db_fgets(char* buf, int max_count, DWORD file) { + char* result; + __asm { + mov eax, buf; + mov edx, max_count; + mov ebx, file; + call _db_fgets; + mov result, eax; + } + return result; +} +static void db_fclose(DWORD file) { + __asm { + mov eax, file; + call _db_fclose; + } +} +static short db_freadShort(DWORD file) { + short rout=0; + __asm { + mov eax, file; + lea edx, rout; + call _db_freadShort; + } + return rout; +} +static void db_freadByteCount(DWORD file, void* cptr, int count) { + __asm { + mov eax, file; + mov edx, cptr; + mov ebx, count; + call _db_freadByteCount; + } +} +static void db_fwriteByteCount(DWORD file, void* cptr, int count) { + __asm { + mov eax, file; + mov edx, cptr; + mov ebx, count; + call _db_fwriteByteCount; + } +} +static void db_fseek(DWORD file, long pos/*, int origin*/) { + __asm { + mov eax, file; + mov edx, pos; + xor ebx, ebx; + call _db_fseek; + } +} +static void* mem_realloc(void* lpmem, DWORD msize) { + void* result; + __asm { + mov eax, lpmem; + mov edx, msize; + call _mem_realloc; + mov result, eax; + } + return result; +} + +typedef int (_stdcall *functype)(); +static const functype _art_init=(functype)0x418840; +static BYTE* mask; +static void CreateMask() { + mask=new BYTE[80*36]; + DWORD file=db_fopen("art\\tiles\\grid000.frm", "r"); + db_fseek(file, 0x4a); + db_freadByteCount(file, mask, 80*36); + db_fclose(file); +} +static WORD ByteSwapW(WORD w) { return ((w&0xff) << 8) | ((w&0xff00) >> 8); } +static DWORD ByteSwapD(DWORD w) { return ((w&0xff) << 24) | ((w&0xff00) << 8) | ((w&0xff0000) >> 8) | ((w&0xff000000) >> 24); } +static int ProcessTile(sArt* tiles, int tile, int listpos) { + char buf[32]; + //sprintf_s(buf, "art\\tiles\\%s", &tiles->names[13*tile]); + strcpy_s(buf, "art\\tiles\\"); + strcat_s(buf, &tiles->names[13*tile]); + + DWORD art=db_fopen(buf, "r"); + if(!art) return 0; + db_fseek(art, 0x3e); + int width=db_freadShort(art); //80; + if(width==80) { + db_fclose(art); + return 0; + } + int height=db_freadShort(art); //36 + db_fseek(art, 0x4A); + BYTE* pixeldata=new BYTE[width*height]; + db_freadByteCount(art, pixeldata, width*height); + DWORD listid=listpos-tiles->total; + float newwidth = (float)(width - width%8); + float newheight = (float)(height - height%12); + int xsize = (int)floor(newwidth/32.0f - newheight/24.0f); + int ysize = (int)floor(newheight/16.0f - newwidth/64.0f); + for(int y=0;ytotal; + origTileCount=listpos; + overrides=new OverrideEntry*[listpos]; + ZeroMemory(overrides, 4*(listpos-1)); + + if(tileMode==2) { + DWORD file=db_fopen("art\\tiles\\xltiles.lst", "rt"); + if(!file) return 0; + DWORD id; + char* comment; + while(db_fgets(buf, 31, file)>0) { + if(comment=strchr(buf, ';')) *comment=0; + id=atoi(buf); + if(id>1) listpos+=ProcessTile(tiles, id, listpos); + } + db_fclose(file); + } else { + for(int i=2;itotal;i++) listpos+=ProcessTile(tiles, i, listpos); + } + if(listpos!=tiles->total) { + tiles->names=(char*)mem_realloc(tiles->names, listpos*13); + for(DWORD i=tiles->total;inames[i*13], 12, "zzz%04d.frm", i-tiles->total); + } + tiles->total=listpos; + } + + delete[] mask; + return 0; +} +static void __declspec(naked) ArtInitHook() { + __asm { + pushad; + mov eax, dword ptr dS:[0x51DEEC]; + push eax; + xor eax, eax; + mov dword ptr ds:[0x51DEEC], eax; + call ArtInitHook2; + pop eax; + mov dword ptr ds:[0x51DEEC], eax; + popad; + xor eax, eax; + retn; + } +} + +struct tilestruct { short tile[2]; }; +static void _stdcall SquareLoadCheck(tilestruct* data) { + for(DWORD y=0;y<100;y++) { + for(DWORD x=0;x<100;x++) { + for(DWORD z=0;z<2;z++) { + DWORD tile=data[y*100+x].tile[z]; + if(tile>1&&tilereplacementid - 1; + for(DWORD y2=0;y2ytiles;y2++) { + for(DWORD x2=0;x2xtiles;x2++) { + newtile++; + if(x-x2<0||y-y2<0) continue; + data[(y-y2)*100+x-x2].tile[z]=(short)newtile; + } + } + } + } + } + } +} + +static void __declspec(naked) SquareLoadHook() { + __asm { + mov edi, edx; + call _db_freadIntCount; + test eax, eax; + jnz end; + pushad; + push edi; + call SquareLoadCheck; + popad; +end: + retn; + } +} + +void TilesInit() { + tileMode=GetPrivateProfileIntA("Misc", "AllowLargeTiles", 0, ini); + if(!tileMode) return; + HookCall(0x481D72, &ArtInitHook); + HookCall(0x48434C, SquareLoadHook); +} diff --git a/sfall/Tiles.h b/sfall/Tiles.h new file mode 100644 index 00000000..ec3c5002 --- /dev/null +++ b/sfall/Tiles.h @@ -0,0 +1,21 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void TilesInit(); \ No newline at end of file diff --git a/sfall/ddraw.cpp b/sfall/ddraw.cpp new file mode 100644 index 00000000..e1210364 --- /dev/null +++ b/sfall/ddraw.cpp @@ -0,0 +1,32 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +typedef HRESULT (_stdcall *DDrawCreateProc)(void* a, void* b, void* c); + +HRESULT _stdcall FakeDirectDrawCreate(void* a, void* b, void* c) { + char path[MAX_PATH]; + GetSystemDirectoryA(path,MAX_PATH); + strcat_s(path, "\\ddraw.dll"); + HMODULE ddraw=LoadLibraryA(path); + if(!ddraw||ddraw==INVALID_HANDLE_VALUE) return -1; + DDrawCreateProc proc=(DDrawCreateProc)GetProcAddress(ddraw, "DirectDrawCreate"); + if(!proc) return -1; + return proc(a,b,c); +} diff --git a/sfall/ddraw.sln b/sfall/ddraw.sln new file mode 100644 index 00000000..fb7fc262 --- /dev/null +++ b/sfall/ddraw.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddraw", "ddraw.vcxproj", "{FB9023DA-CA5E-4704-B601-407810963EF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Trace|Win32 = Trace|Win32 + Win2K|Win32 = Win2K|Win32 + Win2K_Trace|Win32 = Win2K_Trace|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FB9023DA-CA5E-4704-B601-407810963EF0}.Debug|Win32.ActiveCfg = Debug|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Debug|Win32.Build.0 = Debug|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Release|Win32.ActiveCfg = Release|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Release|Win32.Build.0 = Release|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Trace|Win32.ActiveCfg = Trace|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Trace|Win32.Build.0 = Trace|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Win2K|Win32.ActiveCfg = Win2K|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Win2K|Win32.Build.0 = Win2K|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Win2K_Trace|Win32.ActiveCfg = Win2K_Trace|Win32 + {FB9023DA-CA5E-4704-B601-407810963EF0}.Win2K_Trace|Win32.Build.0 = Win2K_Trace|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj new file mode 100644 index 00000000..1eeca7e8 --- /dev/null +++ b/sfall/ddraw.vcxproj @@ -0,0 +1,450 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Trace + Win32 + + + Win2K + Win32 + + + Win2K_Trace + Win32 + + + + {FB9023DA-CA5E-4704-B601-407810963EF0} + sfall + Win32Proj + + + + DynamicLibrary + NotSet + true + v100 + + + DynamicLibrary + NotSet + true + v100 + + + DynamicLibrary + NotSet + true + v90 + + + DynamicLibrary + NotSet + true + v90 + + + DynamicLibrary + NotSet + v100 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + false + false + false + false + true + true + false + false + C:\DXSDK_Feb2010\Include;$(IncludePath) + C:\DXSDK_Feb2010\Lib\x86;$(LibraryPath) + C:\DXSDK_Feb2010\Include;$(IncludePath) + C:\DXSDK_Feb2010\Lib\x86;$(LibraryPath) + + + + Disabled + INITGUID;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;TRACE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + true + + + kernel32.lib;user32.lib;d3d9.lib;d3dx9d.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) + exports.def + ws2_32.dll;d3d9.dll;d3dx9d_43.dll;%(DelayLoadDLLs) + true + Console + false + false + + + false + + + MachineX86 + + + false + %(IgnoreSpecificDefaultLibraries) + + + copy "$(TargetPath)" "C:\games\fallout2\ddraw.dll" + + + + + Full + AnySuitable + true + Speed + true + true + INITGUID;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Sync + MultiThreaded + false + NotSet + Fast + false + false + + + Level3 + ProgramDatabase + Cdecl + + + kernel32.lib;user32.lib;d3d9.lib;d3dx9.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + %(IgnoreSpecificDefaultLibraries) + exports.def + ws2_32.dll;d3d9.dll;d3dx9_43.dll;%(DelayLoadDLLs) + false + Console + true + true + + + false + false + + + MachineX86 + false + + + copy "$(TargetPath)" "C:\games\fallout2\ddraw.dll" + + + + + Full + AnySuitable + true + Speed + true + true + INITGUID;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;TRACE;%(PreprocessorDefinitions) + true + Sync + MultiThreaded + false + NotSet + Fast + false + false + + + Level3 + ProgramDatabase + Cdecl + + + kernel32.lib;user32.lib;d3d9.lib;d3dx9.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + %(IgnoreSpecificDefaultLibraries) + exports.def + ws2_32.dll;d3d9.dll;d3dx9_43.dll;%(DelayLoadDLLs) + false + Console + true + true + + + false + false + + + MachineX86 + false + + + copy "$(TargetPath)" "C:\games\fallout2\ddraw.dll" + + + + + Full + AnySuitable + true + Speed + true + true + INITGUID;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;WIN2K;%(PreprocessorDefinitions) + true + Sync + MultiThreaded + false + NotSet + Fast + false + false + + + Level3 + ProgramDatabase + Cdecl + + + kernel32.lib;user32.lib;d3d9.lib;d3dx9.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + %(IgnoreSpecificDefaultLibraries) + exports.def + ws2_32.dll;d3d9.dll;d3dx9_42.dll;%(DelayLoadDLLs) + false + Console + true + true + + + false + false + + + MachineX86 + false + + + copy "$(TargetPath)" "C:\games\fallout2\ddraw.dll" + + + WIN2K;%(PreprocessorDefinitions) + + + + + Full + AnySuitable + true + Speed + true + true + INITGUID;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;WIN2K;TRACE;%(PreprocessorDefinitions) + true + Sync + MultiThreaded + false + NotSet + Fast + false + false + + + Level3 + ProgramDatabase + Cdecl + + + kernel32.lib;user32.lib;d3d9.lib;d3dx9.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(ProjectName).dll + %(IgnoreSpecificDefaultLibraries) + exports.def + ws2_32.dll;d3d9.dll;d3dx9_42.dll;%(DelayLoadDLLs) + false + Console + true + true + + + false + false + + + MachineX86 + false + + + copy "$(TargetPath)" "C:\games\fallout2\ddraw.dll" + + + WIN2K;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters new file mode 100644 index 00000000..eaafb24e --- /dev/null +++ b/sfall/ddraw.vcxproj.filters @@ -0,0 +1,335 @@ + + + + + {25899f09-c740-49b7-b2ab-b8576c4df621} + + + {0e01fde3-77b4-4b8f-a0c5-71f211b14cf7} + + + {07dcfbfe-dd6b-4b88-8b9e-c512c333406a} + + + {34e14844-5e67-4d8c-8817-89d2d1c23639} + + + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers\ScriptOps + + + Headers + + + Headers + + + Headers + + + Headers + + + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source\win9x + + + Source + + + + Source + + + Source + + + Source + + + Source + + + Source\win9x + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + Source + + + + + + + + + \ No newline at end of file diff --git a/sfall/ddraw2.cpp b/sfall/ddraw2.cpp new file mode 100644 index 00000000..afd476f0 --- /dev/null +++ b/sfall/ddraw2.cpp @@ -0,0 +1,911 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include "ddraw.h" +#include "Graphics.h" +#include "Version.h" +#include "input.h" +#include "math.h" +#include "vector9x.cpp" +#include "LoadGameHook.h" +#include "heads.h" + +#ifdef _DEBUG +#define D3D_DEBUG_INFO +#define DEBUGMESS(a) OutputDebugStringA(a) +#else +#define DEBUGMESS(a) +#endif + +#include +#include + +typedef HRESULT (_stdcall *DDrawCreateProc)(void*, IDirectDraw**, void*); +typedef IDirect3D9* (_stdcall *D3DCreateProc)(UINT version); + +#define UNUSEDFUNCTION { DEBUGMESS("Unused function called: " __FUNCTION__); return DDERR_GENERIC; } +#define SAFERELEASE(a) { if(a) { a->Release(); a=0; } } + +static DWORD ResWidth; +static DWORD ResHeight; +static DWORD GPUBlt; + +static bool DeviceLost=false; +static DDSURFACEDESC surfaceDesc; +static DDSURFACEDESC movieDesc; + +static DWORD palette[256]; + +static DWORD gWidth; +static DWORD gHeight; + +static int ScrollWindowKey; +static DWORD windowLeft=0; +static DWORD windowTop=0; + +static DWORD ShaderVersion; + +static HWND window; +IDirect3D9* d3d9=0; +IDirect3DDevice9* d3d9Device=0; +static IDirect3DTexture9* Tex=0; +static IDirect3DTexture9* sTex1=0; +static IDirect3DTexture9* sTex2=0; +static IDirect3DSurface9* sSurf1=0; +static IDirect3DSurface9* sSurf2=0; +static IDirect3DSurface9* backbuffer=0; +static IDirect3DVertexBuffer9* vBuffer; +static IDirect3DVertexBuffer9* vBuffer2; +static IDirect3DVertexBuffer9* movieBuffer; +static IDirect3DTexture9* gpuPalette; +static IDirect3DTexture9* movieTex=0; + +static ID3DXEffect* gpuBltEffect; +static const char* gpuEffect= +"texture image;\n" +"texture palette;\n" +"texture head;\n" +"sampler s0 = sampler_state { texture=; MAGFILTER=POINT; MINFILTER=POINT; };\n" +"sampler s1 = sampler_state { texture=; MAGFILTER=POINT; MINFILTER=POINT; };\n" +"sampler s2 = sampler_state { texture=; MAGFILTER=POINT; MINFILTER=POINT; };\n" +"float2 size;\n" +"float2 corner;\n" +"float4 P0( in float2 Tex : TEXCOORD0 ) : COLOR0 {\n" +" float3 result = tex1D(s1, tex2D(s0, Tex).a);\n" +" return float4(result.b, result.g, result.r, 1);\n" +"}\n" +"float4 P1( in float2 Tex : TEXCOORD0 ) : COLOR0 {\n" +" float backdrop = tex2D(s0, Tex).a;\n" +" float3 result;\n" +" if(abs(backdrop-(48.0/255.0))<0.001) {\n" +//" float2 size = float2(388.0/640.0, 200.0/480.0);\n" +//" float2 corner = float2(126.0/640.0, 14.0/480.0);\n" +" result = tex2D(s2, saturate((Tex-corner)/size));\n" +" } else {\n" +" result = tex1D(s1, backdrop);\n" +" result = float3(result.b, result.g, result.r);\n" +" }\n" +" return float4(result.r, result.g, result.b, 1);\n" +"}\n" +"technique T0\n" +"{\n" +" pass p0 { PixelShader = compile ps_2_0 P0(); }\n" +"}\n" +"technique T1\n" +"{\n" +" pass p1 { PixelShader = compile ps_2_0 P1(); }\n" +"}\n" +; + +static D3DXHANDLE gpuBltBuf; +static D3DXHANDLE gpuBltPalette; +static D3DXHANDLE gpuBltHead; +static D3DXHANDLE gpuBltHeadSize; +static D3DXHANDLE gpuBltHeadCorner; + +static float rcpres[2]; + +DWORD GraphicsMode; + +struct sShader { + ID3DXEffect* Effect; + bool Active; + D3DXHANDLE ehTicks; + DWORD mode; + DWORD mode2; + + sShader() { + Effect=0; + Active=false; + ehTicks=0; + mode=0; + mode2=0; + } +}; +static vector shaders; +static vector shaderTextures; + +#define MYVERTEXFORMAT D3DFVF_XYZRHW|D3DFVF_TEX1 +struct MyVertex { + float x,y,z,w,u,v; +}; +static MyVertex ShaderVertices[] = { + {-0.5, -0.5, 0, 1, 0, 0}, + {-0.5, 479.5, 0, 1, 0, 1}, + {639.5, -0.5, 0, 1, 1, 0}, + {639.5, 479.5, 0, 1, 1, 1} +}; +void GetFalloutWindowInfo(DWORD* width, DWORD* height, HWND* wnd) { + *width=gWidth; + *height=gHeight; + *wnd=window; +} +int _stdcall GetShaderVersion() { return ShaderVersion; } +void _stdcall SetShaderMode(DWORD d, DWORD mode) { + if(d>=shaders.size()||!shaders[d].Effect) return; + if(mode&0x80000000) { + shaders[d].mode2=mode^0x80000000; + } else { + shaders[d].mode=mode; + } +} + +int _stdcall LoadShader(const char* path) { + if(GraphicsMode < 4) return -1; + if(strstr(path, "..")) return -1; + if(strstr(path, ":")) return -1; + char buf[MAX_PATH]; + strcpy_s(buf, "data\\shaders\\"); + strcat_s(buf, path); + for(DWORD d=0;dSetFloatArray("rcpres", rcpres, 2); + + for(int i=1;i<128;i++) { + char buf[MAX_PATH]; + const char* name; + IDirect3DTexture9* tex; + + sprintf_s(buf, "texname%d", i); + if(FAILED(shader.Effect->GetString(buf, &name))) break; + sprintf_s(buf, "data\\art\\stex\\%s", name); + if(FAILED(D3DXCreateTextureFromFileA(d3d9Device,buf,&tex))) continue; + sprintf_s(buf, "tex%d", i); + shader.Effect->SetTexture(buf, tex); + shaderTextures.push_back(tex); + } + + shader.ehTicks=shader.Effect->GetParameterByName(0, "tickcount"); + shaders.push_back(shader); + return shaders.size()-1; +} + +void _stdcall ActivateShader(DWORD d) { if(d=shaders.size()||!shaders[d].Effect||id<1||id>128) return -1; + IDirect3DBaseTexture9* tex=0; + char buf[8]; + buf[0]='t'; buf[1]='e'; buf[2]='x'; + _itoa_s(id, &buf[3], 4, 10); + if(FAILED(shaders[d].Effect->GetTexture(buf, &tex))) return -1; + if(!tex) return -1; + tex->Release(); + for(DWORD i=0;i=shaders.size()||!shaders[d].Effect) return; + shaders[d].Effect->SetInt(param, value); +} +void _stdcall SetShaderFloat(DWORD d, const char* param, float value) { + if(d>=shaders.size()||!shaders[d].Effect) return; + shaders[d].Effect->SetFloat(param, value); +} +void _stdcall SetShaderVector(DWORD d, const char* param, float f1, float f2, float f3, float f4) { + if(d>=shaders.size()||!shaders[d].Effect) return; + shaders[d].Effect->SetFloatArray(param, &f1, 4); +} + +void _stdcall SetShaderTexture(DWORD d, const char* param, DWORD value) { + if(d>=shaders.size()||!shaders[d].Effect||value>=shaderTextures.size()) return; + shaders[d].Effect->SetTexture(param, shaderTextures[value]); +} +static void ResetDevice(bool CreateNew) { + D3DPRESENT_PARAMETERS params; + ZeroMemory(¶ms, sizeof(params)); + params.BackBufferCount=1; + params.BackBufferFormat=(GraphicsMode==5)?D3DFMT_UNKNOWN:D3DFMT_X8R8G8B8; + params.BackBufferWidth=gWidth; + params.BackBufferHeight=gHeight; + params.EnableAutoDepthStencil=false; + params.MultiSampleQuality=0; + params.MultiSampleType=D3DMULTISAMPLE_NONE; + params.Windowed=(GraphicsMode==5); + params.SwapEffect=D3DSWAPEFFECT_DISCARD; + params.hDeviceWindow=window; + params.PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE; + + bool software=false; + if(CreateNew) { + if(FAILED(d3d9->CreateDevice(0,D3DDEVTYPE_HAL,window,D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,¶ms,&d3d9Device))) { + software=true; + d3d9->CreateDevice(0,D3DDEVTYPE_HAL,window,D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,¶ms,&d3d9Device); + } + D3DCAPS9 caps; + d3d9Device->GetDeviceCaps(&caps); + ShaderVersion=((caps.PixelShaderVersion&0x0000ff00)>>8)*10 + (caps.PixelShaderVersion&0xff); + + if(GPUBlt==2) { + if(ShaderVersion<20) GPUBlt=0; + } + + if(GPUBlt) { + D3DXCreateEffect(d3d9Device, gpuEffect, strlen(gpuEffect), 0, 0, 0, 0, &gpuBltEffect, 0); + gpuBltBuf=gpuBltEffect->GetParameterByName(0, "image"); + gpuBltPalette=gpuBltEffect->GetParameterByName(0, "palette"); + gpuBltHead=gpuBltEffect->GetParameterByName(0, "head"); + gpuBltHeadSize=gpuBltEffect->GetParameterByName(0, "size"); + gpuBltHeadCorner=gpuBltEffect->GetParameterByName(0, "corner"); + } + } else { + d3d9Device->Reset(¶ms); + if(gpuBltEffect) gpuBltEffect->OnResetDevice(); + for(DWORD d=0;dOnResetDevice(); + } + } + + ShaderVertices[1].y=ResHeight-0.5f; + ShaderVertices[2].x=ResWidth-0.5f; + ShaderVertices[3].y=ResHeight-0.5f; + ShaderVertices[3].x=ResWidth-0.5f; + d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, GPUBlt?D3DFMT_A8:D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &Tex, 0); + d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &sTex1, 0); + d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &sTex2, 0); + if(GPUBlt) { + d3d9Device->CreateTexture(256, 1, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &gpuPalette, 0); + gpuBltEffect->SetTexture(gpuBltBuf, Tex); + gpuBltEffect->SetTexture(gpuBltPalette, gpuPalette); + } + + sTex1->GetSurfaceLevel(0, &sSurf1); + sTex2->GetSurfaceLevel(0, &sSurf2); + d3d9Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); + + d3d9Device->CreateVertexBuffer(4*sizeof(MyVertex),D3DUSAGE_WRITEONLY|(software?D3DUSAGE_SOFTWAREPROCESSING:0),MYVERTEXFORMAT,D3DPOOL_DEFAULT,&vBuffer, 0); + byte* VertexPointer; + vBuffer->Lock(0,0,(void**)&VertexPointer,0); + CopyMemory(VertexPointer,ShaderVertices,sizeof(ShaderVertices)); + vBuffer->Unlock(); + + d3d9Device->CreateVertexBuffer(4*sizeof(MyVertex),D3DUSAGE_WRITEONLY|(software?D3DUSAGE_SOFTWAREPROCESSING:0),MYVERTEXFORMAT,D3DPOOL_DEFAULT,&movieBuffer, 0); + + MyVertex ShaderVertices2[4] = { + ShaderVertices[0], + ShaderVertices[1], + ShaderVertices[2], + ShaderVertices[3] + }; + + ShaderVertices2[1].y=(float)gHeight-0.5f; + ShaderVertices2[2].x=(float)gWidth-0.5f; + ShaderVertices2[3].y=(float)gHeight-0.5f; + ShaderVertices2[3].x=(float)gWidth-0.5f; + + d3d9Device->CreateVertexBuffer(4*sizeof(MyVertex),D3DUSAGE_WRITEONLY|(software?D3DUSAGE_SOFTWAREPROCESSING:0),MYVERTEXFORMAT,D3DPOOL_DEFAULT,&vBuffer2, 0); + vBuffer2->Lock(0,0,(void**)&VertexPointer,0); + CopyMemory(VertexPointer,ShaderVertices2,sizeof(ShaderVertices2)); + vBuffer2->Unlock(); + + d3d9Device->SetFVF(MYVERTEXFORMAT); + d3d9Device->SetTexture(0, Tex); + d3d9Device->SetStreamSource(0, vBuffer,0, sizeof(MyVertex)); + + d3d9Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); + d3d9Device->SetRenderState(D3DRS_ALPHATESTENABLE, false); + d3d9Device->SetRenderState(D3DRS_ZENABLE, false); + d3d9Device->SetRenderState(D3DRS_CULLMODE,2); +} +static void Present() { + if(ScrollWindowKey!=0&&((ScrollWindowKey>0&&KeyDown((BYTE)ScrollWindowKey)) + ||(ScrollWindowKey==-1&&(KeyDown(DIK_LCONTROL)||KeyDown(DIK_RCONTROL))) + ||(ScrollWindowKey==-2&&(KeyDown(DIK_LMENU)||KeyDown(DIK_RMENU))) + ||(ScrollWindowKey==-3&&(KeyDown(DIK_LSHIFT)||KeyDown(DIK_RSHIFT))))) { + int winx, winy; + GetMouse(&winx, &winy); + windowLeft+=winx; + windowTop+=winy; + RECT r, r2; + r.left=windowLeft; + r.right=windowLeft+gWidth; + r.top=windowTop; + r.bottom=windowTop+gHeight; + AdjustWindowRect(&r, WS_OVERLAPPED|WS_CAPTION|WS_BORDER, false); + r.right-=(r.left-windowLeft); + r.left=windowLeft; + r.bottom-=(r.top-windowTop); + r.top=windowTop; + if(GetWindowRect(GetShellWindow(), &r2)) { + if(r.right>r2.right) { + DWORD move=r.right-r2.right; + r.left-=move; + r.right-=move; + windowLeft-=move; + } + if(r.leftr2.bottom) { + DWORD move=r.bottom-r2.bottom; + r.top-=move; + r.bottom-=move; + windowTop-=move; + } + if(r.topPresent(0,0,0,0)==D3DERR_DEVICELOST) { + d3d9Device->SetTexture(0, 0); + SAFERELEASE(Tex) + SAFERELEASE(backbuffer); + SAFERELEASE(sSurf1); + SAFERELEASE(sSurf2); + SAFERELEASE(sTex1); + SAFERELEASE(sTex2); + SAFERELEASE(vBuffer); + SAFERELEASE(vBuffer2); + SAFERELEASE(movieBuffer); + SAFERELEASE(gpuPalette); + if(gpuBltEffect) gpuBltEffect->OnLostDevice(); + for(DWORD d=0;dOnLostDevice(); + } + DeviceLost=true; + } +} + +void RefreshGraphics() { + if(DeviceLost) return; + //Tex->UnlockRect(0); + d3d9Device->BeginScene(); + //d3d9Device->SetFVF(D3DFVF_XYZRHW|D3DFVF_TEX1); + d3d9Device->SetStreamSource(0, vBuffer, 0, sizeof(MyVertex)); + d3d9Device->SetRenderTarget(0, sSurf1); + if(GPUBlt&&shaders.size()) { + UINT unused; + gpuBltEffect->Begin(&unused, 0); + gpuBltEffect->BeginPass(0); + d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + gpuBltEffect->EndPass(); + gpuBltEffect->End(); + d3d9Device->StretchRect(sSurf1, 0, sSurf2, 0, D3DTEXF_NONE); + d3d9Device->SetTexture(0, sTex2); + } else { + d3d9Device->SetTexture(0, Tex); + } + for(int d=shaders.size()-1;d>=0;d--) { + if(!shaders[d].Effect||!shaders[d].Active) continue; + if(shaders[d].mode2&&!(shaders[d].mode2&GetCurrentLoops())) continue; + if(shaders[d].mode&GetCurrentLoops()) continue; + if(shaders[d].ehTicks) shaders[d].Effect->SetInt(shaders[d].ehTicks, GetTickCount()); + UINT passes; + shaders[d].Effect->Begin(&passes, 0); + for(DWORD pass=0;passBeginPass(pass); + d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + shaders[d].Effect->EndPass(); + d3d9Device->StretchRect(sSurf1, 0, sSurf2, 0, D3DTEXF_NONE); + d3d9Device->SetTexture(0, sTex2); + } + shaders[d].Effect->End(); + d3d9Device->SetTexture(0, sTex2); + } + d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(MyVertex)); + d3d9Device->SetRenderTarget(0, backbuffer); + if(GPUBlt&&!shaders.size()) { + UINT unused; + gpuBltEffect->Begin(&unused, 0); + gpuBltEffect->BeginPass(0); + } + d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + if(GPUBlt) { + gpuBltEffect->EndPass(); + gpuBltEffect->End(); + } + d3d9Device->EndScene(); + Present(); +} +void SetMovieTexture(void* tex) { + movieTex=(IDirect3DTexture9*)tex; + D3DSURFACE_DESC desc; + movieTex->GetLevelDesc(0, &desc); + float aspect = (float)desc.Width/(float)desc.Height; + float winaspect=(float)gWidth/(float)gHeight; + + byte* VertexPointer; + movieBuffer->Lock(0,0,(void**)&VertexPointer,0); + MyVertex ShaderVertices2[4] = { + ShaderVertices[0], + ShaderVertices[1], + ShaderVertices[2], + ShaderVertices[3] + }; + + ShaderVertices2[1].y=(float)gHeight-0.5f; + ShaderVertices2[2].x=(float)gWidth-0.5f; + ShaderVertices2[3].y=(float)gHeight-0.5f; + ShaderVertices2[3].x=(float)gWidth-0.5f; + + DWORD gap; + if(aspect>winaspect) { + aspect=(float)desc.Width/(float)gWidth; + desc.Height=(int)(desc.Height/aspect); + gap=(gHeight-desc.Height)/2; + + ShaderVertices2[0].y+=gap; + ShaderVertices2[2].y+=gap; + ShaderVertices2[1].y-=gap; + ShaderVertices2[3].y-=gap; + } else if(aspectUnlock(); +} +void _stdcall PlayMovieFrame() { + d3d9Device->SetTexture(0, movieTex); + d3d9Device->SetStreamSource(0, movieBuffer, 0, sizeof(MyVertex)); + d3d9Device->BeginScene(); + d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + d3d9Device->EndScene(); + Present(); +} +void _stdcall SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff) { + if(tex) { + gpuBltEffect->SetTexture(gpuBltHead, tex); + float size[2]; + size[0]=((float)width)*rcpres[0]; + size[1]=((float)height)*rcpres[1]; + gpuBltEffect->SetFloatArray(gpuBltHeadSize, size, 2); + size[0]=(126.0f+xoff + (388-width)/2)*rcpres[0]; + size[1]=(14.0f+yoff + (200-height)/2)*rcpres[1]; + gpuBltEffect->SetFloatArray(gpuBltHeadCorner, size, 2); + gpuBltEffect->SetTechnique("T1"); + } else gpuBltEffect->SetTechnique("T0"); +} +void graphics_OnGameLoad() { + for(DWORD d=0;d 256||c==0) return DDERR_INVALIDPARAMS; + if(GPUBlt) { + if(!gpuPalette) { + CopyMemory(&palette[b], d, c*4); + } else { + D3DLOCKED_RECT rect; + if(!FAILED(gpuPalette->LockRect(0, &rect, 0, D3DLOCK_DISCARD))) { + CopyMemory(&palette[b], d, c*4); + CopyMemory(rect.pBits, palette, 256*4); + gpuPalette->UnlockRect(0); + } + } + } else { + CopyMemory(&palette[b], d, c*4); + for(DWORD i=b;ilockTarget; + D3DLOCKED_RECT dRect; + Tex->LockRect(0, &dRect, 0, 0); + if(!GPUBlt) dRect.Pitch/=4; + DWORD yoffset=(ResHeight-320)/2; + DWORD xoffset=(ResWidth-640)/2; + if(GPUBlt) { + char* pBits=(char*)dRect.pBits; + for(DWORD y=0;y<320;y++) { + CopyMemory(&pBits[(y+yoffset)*dRect.Pitch + xoffset], &lockTarget[y*640], 640); + } + for(DWORD y=0;y640) { + for(DWORD y=yoffset;y640) { + for(DWORD y=yoffset;yUnlockRect(0); + if(!DeviceLost) { + d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(MyVertex)); + d3d9Device->SetTexture(0, Tex); + d3d9Device->BeginScene(); + if(GPUBlt) { + UINT unused; + gpuBltEffect->Begin(&unused, 0); + gpuBltEffect->BeginPass(0); + } + d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + if(GPUBlt) { + gpuBltEffect->EndPass(); + gpuBltEffect->End(); + } + d3d9Device->EndScene(); + Present(); + } + return DD_OK; + } + HRESULT _stdcall BltBatch(LPDDBLTBATCH, DWORD, DWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall BltFast(DWORD,DWORD,LPDIRECTDRAWSURFACE, LPRECT,DWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall DeleteAttachedSurface(DWORD,LPDIRECTDRAWSURFACE) { UNUSEDFUNCTION; } + HRESULT _stdcall EnumAttachedSurfaces(LPVOID,LPDDENUMSURFACESCALLBACK) { UNUSEDFUNCTION; } + HRESULT _stdcall EnumOverlayZOrders(DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) { UNUSEDFUNCTION; } + HRESULT _stdcall Flip(LPDIRECTDRAWSURFACE, DWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall GetAttachedSurface(LPDDSCAPS, LPDIRECTDRAWSURFACE *) { UNUSEDFUNCTION; } + HRESULT _stdcall GetBltStatus(DWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall GetCaps(LPDDSCAPS) { UNUSEDFUNCTION; } + HRESULT _stdcall GetClipper(LPDIRECTDRAWCLIPPER *) { UNUSEDFUNCTION; } + HRESULT _stdcall GetColorKey(DWORD, LPDDCOLORKEY) { UNUSEDFUNCTION; } + HRESULT _stdcall GetDC(HDC *) { UNUSEDFUNCTION; } + HRESULT _stdcall GetFlipStatus(DWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall GetOverlayPosition(LPLONG, LPLONG) { UNUSEDFUNCTION; } + HRESULT _stdcall GetPalette(LPDIRECTDRAWPALETTE *) { UNUSEDFUNCTION; } + HRESULT _stdcall GetPixelFormat(LPDDPIXELFORMAT) { UNUSEDFUNCTION; } + HRESULT _stdcall GetSurfaceDesc(LPDDSURFACEDESC) { UNUSEDFUNCTION; } + HRESULT _stdcall Initialize(LPDIRECTDRAW, LPDDSURFACEDESC) { UNUSEDFUNCTION; } + HRESULT _stdcall IsLost() { UNUSEDFUNCTION; } + HRESULT _stdcall Lock(LPRECT a,LPDDSURFACEDESC b,DWORD c,HANDLE d) { + if(!Primary) *b=movieDesc; + else *b=surfaceDesc; + b->lpSurface=lockTarget; + return DD_OK; + } + HRESULT _stdcall ReleaseDC(HDC) { UNUSEDFUNCTION; } + HRESULT _stdcall Restore() { UNUSEDFUNCTION; } + HRESULT _stdcall SetClipper(LPDIRECTDRAWCLIPPER) { UNUSEDFUNCTION; } + HRESULT _stdcall SetColorKey(DWORD, LPDDCOLORKEY) { UNUSEDFUNCTION; } + HRESULT _stdcall SetOverlayPosition(LONG, LONG) { UNUSEDFUNCTION; } + HRESULT _stdcall SetPalette(LPDIRECTDRAWPALETTE) { return DD_OK; } + HRESULT _stdcall Unlock(LPVOID) { + if(Primary&&d3d9Device) { + if(DeviceLost) { + if(d3d9Device->TestCooperativeLevel()==D3DERR_DEVICENOTRESET) { + ResetDevice(false); + DeviceLost=false; + } + } + if(!DeviceLost) { + D3DLOCKED_RECT dRect; + Tex->LockRect(0, &dRect, 0, 0); + if(!GPUBlt) dRect.Pitch/=4; + if(GPUBlt) { + char* target=(char*)dRect.pBits; + for(DWORD y=0;yUnlockRect(0); + RefreshGraphics(); + } + } + return DD_OK; + } + HRESULT _stdcall UpdateOverlay(LPRECT, LPDIRECTDRAWSURFACE,LPRECT,DWORD, LPDDOVERLAYFX) { UNUSEDFUNCTION; } + HRESULT _stdcall UpdateOverlayDisplay(DWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall UpdateOverlayZOrder(DWORD, LPDIRECTDRAWSURFACE) { UNUSEDFUNCTION; } +}; + +class FakeDirectDraw2 : IDirectDraw +{ +private: + ULONG Refs; +public: + FakeDirectDraw2() { + Refs=1; + } + // IUnknown methods + HRESULT _stdcall QueryInterface(REFIID, LPVOID*) { return E_NOINTERFACE; } + ULONG _stdcall AddRef() { return ++Refs; } + ULONG _stdcall Release() { + if(!--Refs) { + for(DWORD d=0;dRelease(); + shaders.clear(); + shaderTextures.clear(); + SAFERELEASE(backbuffer); + SAFERELEASE(sSurf1); + SAFERELEASE(sSurf2); + SAFERELEASE(Tex); + SAFERELEASE(sTex1); + SAFERELEASE(sTex2); + SAFERELEASE(vBuffer); + SAFERELEASE(vBuffer2); + SAFERELEASE(d3d9Device); + SAFERELEASE(d3d9); + SAFERELEASE(gpuPalette); + SAFERELEASE(gpuBltEffect); + SAFERELEASE(movieBuffer); + SAFERELEASE(movieTex); + delete this; + return 0; + } else return Refs; + } + // IDirectDraw methods + HRESULT _stdcall Compact() { UNUSEDFUNCTION; } + HRESULT _stdcall CreateClipper(DWORD, LPDIRECTDRAWCLIPPER*, IUnknown*) { UNUSEDFUNCTION; } + HRESULT _stdcall CreatePalette(DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE* c, IUnknown*) { + *c=(IDirectDrawPalette*)new FakePalette2(); + return DD_OK; + } + HRESULT _stdcall CreateSurface(LPDDSURFACEDESC a, LPDIRECTDRAWSURFACE * b, IUnknown * c) { + if(a->dwFlags==1&&a->ddsCaps.dwCaps==DDSCAPS_PRIMARYSURFACE) *b=(IDirectDrawSurface*)new FakeSurface2(true); + else *b=(IDirectDrawSurface*)new FakeSurface2(false); + return DD_OK; + } + HRESULT _stdcall DuplicateSurface(LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE *) { UNUSEDFUNCTION; } + HRESULT _stdcall EnumDisplayModes(DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK) { UNUSEDFUNCTION; } + HRESULT _stdcall EnumSurfaces(DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK) { UNUSEDFUNCTION; } + HRESULT _stdcall FlipToGDISurface() { UNUSEDFUNCTION; } + HRESULT _stdcall GetCaps(LPDDCAPS, LPDDCAPS b) { UNUSEDFUNCTION; } + HRESULT _stdcall GetDisplayMode(LPDDSURFACEDESC) { UNUSEDFUNCTION; } + HRESULT _stdcall GetFourCCCodes(LPDWORD,LPDWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall GetGDISurface(LPDIRECTDRAWSURFACE *) { UNUSEDFUNCTION; } + HRESULT _stdcall GetMonitorFrequency(LPDWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall GetScanLine(LPDWORD) { UNUSEDFUNCTION; } + HRESULT _stdcall GetVerticalBlankStatus(LPBOOL) { UNUSEDFUNCTION; } + HRESULT _stdcall Initialize(GUID *) { UNUSEDFUNCTION; } + HRESULT _stdcall RestoreDisplayMode() { return DD_OK; } + HRESULT _stdcall SetCooperativeLevel(HWND a, DWORD b) { + window=a; + + if(!d3d9Device) { + ResetDevice(true); + CoInitialize(0); + + if(GPUBlt) HeadsInit(); + } + + if(GraphicsMode==5) { + SetWindowLong(a, GWL_STYLE, WS_OVERLAPPED|WS_CAPTION|WS_BORDER); + RECT r; + r.left=0; + r.right=gWidth; + r.top=0; + r.bottom=gHeight; + AdjustWindowRect(&r, WS_OVERLAPPED|WS_CAPTION|WS_BORDER, false); + r.right-=r.left; + r.left=0; + r.bottom-=r.top; + r.top=0; + SetWindowPos(a, HWND_NOTOPMOST, 0, 0, r.right, r.bottom,SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_SHOWWINDOW); + } + return DD_OK; + } + HRESULT _stdcall SetDisplayMode(DWORD, DWORD, DWORD) { return DD_OK; } + HRESULT _stdcall WaitForVerticalBlank(DWORD, HANDLE) { UNUSEDFUNCTION; } +}; + +HRESULT _stdcall FakeDirectDrawCreate2(void*, IDirectDraw** b, void*) { + ResWidth=*(DWORD*)0x004CAD6B; + ResHeight=*(DWORD*)0x004CAD66; + + if(!d3d9) { + d3d9=Direct3DCreate9(D3D_SDK_VERSION); + } + + ZeroMemory(&surfaceDesc, sizeof(DDSURFACEDESC)); + surfaceDesc.dwSize=sizeof(DDSURFACEDESC); + surfaceDesc.dwFlags=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_PITCH; + surfaceDesc.dwWidth=ResWidth; + surfaceDesc.dwHeight=ResHeight; + surfaceDesc.ddpfPixelFormat.dwRGBBitCount=16; + surfaceDesc.ddpfPixelFormat.dwSize=sizeof(DDPIXELFORMAT); + surfaceDesc.ddpfPixelFormat.dwRBitMask=0xf800; + surfaceDesc.ddpfPixelFormat.dwGBitMask=0x7e0; + surfaceDesc.ddpfPixelFormat.dwBBitMask=0x1f; + surfaceDesc.ddpfPixelFormat.dwFlags=DDPF_RGB; + surfaceDesc.ddsCaps.dwCaps=DDSCAPS_TEXTURE; + surfaceDesc.lPitch=ResWidth; + movieDesc=surfaceDesc; + movieDesc.lPitch=640; + movieDesc.dwHeight=320; + movieDesc.dwWidth=640; + + gWidth=GetPrivateProfileIntA("Graphics", "GraphicsWidth", 0, ini); + gHeight=GetPrivateProfileIntA("Graphics", "GraphicsHeight", 0, ini); + if(!gWidth||!gHeight) { + gWidth=ResWidth; + gHeight=ResHeight; + } + GPUBlt=GetPrivateProfileIntA("Graphics", "GPUBlt", 0, ini); + if(!GPUBlt || GPUBlt>2) GPUBlt=2; //Swap them around to keep compatibility with old ddraw.ini's + else if(GPUBlt==2) GPUBlt=0; + + if(GraphicsMode==5) { + ScrollWindowKey=GetPrivateProfileInt("Input", "WindowScrollKey", 0, ini); + } else ScrollWindowKey=0; + + rcpres[0]=1.0f/(float)gWidth; + rcpres[1]=1.0f/(float)gHeight; + + *b=(IDirectDraw*)new FakeDirectDraw2(); + return DD_OK; +} diff --git a/sfall/dinput.cpp b/sfall/dinput.cpp new file mode 100644 index 00000000..08a3c53c --- /dev/null +++ b/sfall/dinput.cpp @@ -0,0 +1,334 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#define DIRECTINPUT_VERSION 0x0700 +#include +#include "queue9x.cpp" +#include +#include "graphics.h" +#include "ScriptExtender.h" +#include "HookScripts.h" +#include "DebugEditor.h" +#include "Logging.h" + +typedef HRESULT (_stdcall *DInputCreateProc)(HINSTANCE a,DWORD b,IDirectInputA** c,IUnknown* d); + +static bool UseScrollWheel; +static DWORD WheelMod; + +static bool ReverseMouse; + +static bool MiddleMouseDown; +static DWORD MiddleMouseKey; + +static bool BackgroundKeyboard; +static bool BackgroundMouse; + +static bool AdjustMouseSpeed; +static double MouseSpeedMod; +static double MousePartX; +static double MousePartY; + +#define MAX_KEYS (264) +static DWORD KeysDown[MAX_KEYS]; + +static int mouseX; +static int mouseY; + +static DWORD ForcingGraphicsRefresh=0; + +static DWORD DebugEditorKey=0; + +void _stdcall ForceGraphicsRefresh(DWORD d) { ForcingGraphicsRefresh=(d==0)?0:1; } +void GetMouse(int* x, int* y) { *x=mouseX; *y=mouseY; mouseX=0; mouseY=0; } + +static BYTE LMouse=0; +static BYTE RMouse=0; +static int MPMouseX; +static int MPMouseY; + +#define kDeviceType_KEYBOARD 0 +#define kDeviceType_MOUSE 1 + +static queue bufferedPresses; + +static HKL keyboardLayout; + +void SetMDown(bool down, bool right) { + if(right) RMouse=down?0x80:0; + else LMouse=down?0x80:0; +} +void SetMPos(int x, int y) { + MPMouseX=x; + MPMouseY=y; +} + +DWORD _stdcall KeyDown(DWORD key) { + if ((key & 0x80000000) > 0) { // special flag to check by VK code directly + return GetAsyncKeyState(key & 0xFFFF) & 0x8000; + } + key = key & 0xFFFF; + // combined use of DINPUT states + confirmation from GetAsyncKeyState() + if (key>MAX_KEYS) { + return 0; + } else { + DWORD keyVK = 0; + if (KeysDown[key]) { // confirm pressed state + keyVK = MapVirtualKeyEx(key, MAPVK_VSC_TO_VK, keyboardLayout); + if (keyVK) KeysDown[key] = (GetAsyncKeyState(keyVK) & 0x8000); + } + return (KeysDown[key] > 0); + } +} + +void _stdcall TapKey(DWORD key) { + DIDEVICEOBJECTDATA data; + data.dwTimeStamp=0; + data.dwSequence=0; + data.dwOfs=key; + data.dwData=0x80; + bufferedPresses.push(data); + data.dwData=0x00; + bufferedPresses.push(data); +} + +class FakeDirectInputDevice : public IDirectInputDeviceA { +private: + IDirectInputDeviceA* RealDevice; + DWORD DeviceType; + ULONG Refs; + bool formatLock; + DIDATAFORMAT oldFormat; +public: + /*** Constructor and misc functions ***/ + FakeDirectInputDevice(IDirectInputDevice* device,DWORD type) { + RealDevice=device; + DeviceType=type; + Refs=1; + formatLock = false; + oldFormat.dwDataSize=0; + } + /*** IUnknown methods ***/ + HRESULT _stdcall QueryInterface (REFIID riid, LPVOID * ppvObj) { return RealDevice->QueryInterface(riid,ppvObj); } + ULONG _stdcall AddRef(void) { return ++Refs; } + ULONG _stdcall Release(void) { + if(--Refs==0) { + RealDevice->Release(); + delete this; + return 0; + } else { return Refs; } + } + + /*** IDirectInputDevice8A methods ***/ + HRESULT _stdcall GetCapabilities(LPDIDEVCAPS a) { return RealDevice->GetCapabilities(a); } + HRESULT _stdcall EnumObjects(LPDIENUMDEVICEOBJECTSCALLBACKA a,LPVOID b,DWORD c) { return RealDevice->EnumObjects(a,b,c); } + HRESULT _stdcall GetProperty(REFGUID a,DIPROPHEADER* b) { return RealDevice->GetProperty(a,b); } + HRESULT _stdcall SetProperty(REFGUID a,const DIPROPHEADER* b) { return RealDevice->SetProperty(a,b); } + HRESULT _stdcall Acquire(void) { return RealDevice->Acquire(); } + HRESULT _stdcall Unacquire(void) { return RealDevice->Unacquire(); } + //Only called for the mouse + HRESULT _stdcall GetDeviceState(DWORD a,LPVOID b) { + if(ForcingGraphicsRefresh) RefreshGraphics(); + if(DeviceType!=kDeviceType_MOUSE) { + return RealDevice->GetDeviceState(a,b); + } + + DIMOUSESTATE2 MouseState; + HRESULT hr; + int numButtons; + if(formatLock) hr=RealDevice->GetDeviceState(sizeof(DIMOUSESTATE2), &MouseState); + else hr=RealDevice->GetDeviceState(sizeof(DIMOUSESTATE), &MouseState); + if(FAILED(hr)) return hr; + if(ReverseMouse) { + BYTE tmp=MouseState.rgbButtons[0]; + MouseState.rgbButtons[0]=MouseState.rgbButtons[1]; + MouseState.rgbButtons[1]=tmp; + } + if(AdjustMouseSpeed) { + double d=((double)MouseState.lX)*MouseSpeedMod + MousePartX; + MousePartX = modf(d, &d); + MouseState.lX=(LONG)d; + d=((double)MouseState.lY)*MouseSpeedMod + MousePartY; + MousePartY = modf(d, &d); + MouseState.lY=(LONG)d; + } + if(UseScrollWheel) { + if(MouseState.lZ>0) { + int count; + if(WheelMod) count=MouseState.lZ/WheelMod; + else count=1; + while(count--) TapKey(DIK_UP); + } else if(MouseState.lZ<0) { + int count; + if(WheelMod) count=(-MouseState.lZ)/WheelMod; + else count=1; + while(count--) TapKey(DIK_DOWN); + } + } + if(MiddleMouseKey&&MouseState.rgbButtons[2]) { + if(!MiddleMouseDown) { + TapKey(MiddleMouseKey); + MiddleMouseDown=true; + } + } else MiddleMouseDown=false; + mouseX=MouseState.lX; + mouseY=MouseState.lY; + + numButtons = formatLock ? 8 : 4; + for(int i=0;i 0); + } + KeysDown[256+i]=MouseState.rgbButtons[i]; + } + memcpy(b, &MouseState, sizeof(DIMOUSESTATE)); + return 0; + } + //Only called for the keyboard + HRESULT _stdcall GetDeviceData(DWORD a,DIDEVICEOBJECTDATA* b,DWORD* c,DWORD d) { + if(DeviceType!=kDeviceType_KEYBOARD) { + return RealDevice->GetDeviceData(a,b,c,d); + } + + RunGlobalScripts2(); + + if(!b||bufferedPresses.empty()||(d&DIGDD_PEEK)) { + HRESULT hr=RealDevice->GetDeviceData(a,b,c,d); + if(FAILED(hr)||!b||!(*c)) return hr; + for(DWORD i=0;i<*c;i++) { + KeysDown[b[i].dwOfs]=b[i].dwData&0x80; + KeyPressHook(b[i].dwOfs, (b[i].dwData & 0x80) > 0, MapVirtualKeyEx(b[i].dwOfs, MAPVK_VSC_TO_VK, keyboardLayout)); + } + if(KeysDown[DebugEditorKey]) RunDebugEditor(); + return hr; + } + //Despite passing an array of 32 data objects, fallout cant seem to cope with a key being pressed and released in the same frame... + //TODO: Fallouts behaviour when passing multiple keypresses makes it appear like it's expecting the DIDEVICEOBJECTDATA struct to be + // something other than 16 bytes. afaik, fallout uses DX3 for input, which is before the appData field was added, but it could + // be worth checking anyway. + *b=bufferedPresses.front(); + bufferedPresses.pop(); + *c=1; + return DI_OK; + } + HRESULT _stdcall SetDataFormat(const DIDATAFORMAT* a) { + if(formatLock&&oldFormat.dwSize) return RealDevice->SetDataFormat(&oldFormat); + memcpy(&oldFormat, a, sizeof(DIDATAFORMAT)); + return RealDevice->SetDataFormat(a); + } + HRESULT _stdcall SetEventNotification(HANDLE a) { return RealDevice->SetEventNotification(a); } + HRESULT _stdcall SetCooperativeLevel(HWND a,DWORD b) { + if(DeviceType==kDeviceType_KEYBOARD&&BackgroundKeyboard) b=DISCL_BACKGROUND|DISCL_NONEXCLUSIVE; + if(DeviceType==kDeviceType_MOUSE&&BackgroundMouse) b=DISCL_BACKGROUND|DISCL_NONEXCLUSIVE; + return RealDevice->SetCooperativeLevel(a,b); + } + HRESULT _stdcall GetObjectInfo(LPDIDEVICEOBJECTINSTANCEA a,DWORD b,DWORD c) { return RealDevice->GetObjectInfo(a,b,c); } + HRESULT _stdcall GetDeviceInfo(LPDIDEVICEINSTANCEA a) { return RealDevice->GetDeviceInfo(a); } + HRESULT _stdcall RunControlPanel(HWND a,DWORD b) { return RealDevice->RunControlPanel(a,b); } + HRESULT _stdcall Initialize(HINSTANCE a,DWORD b,REFGUID c) { return RealDevice->Initialize(a,b,c); } + void LockDataFormat(bool lock) {formatLock = lock; } +}; +class FakeDirectInput : public IDirectInputA { +private: + IDirectInputA* RealInput; + ULONG Refs; +public: + /*** Constructor ***/ + FakeDirectInput(IDirectInput* Real) { RealInput=Real; Refs=1; } + /*** IUnknown methods ***/ + HRESULT _stdcall QueryInterface (REFIID riid, LPVOID* ppvObj) { return RealInput->QueryInterface(riid,ppvObj); } + ULONG _stdcall AddRef(void) { return ++Refs; } + ULONG _stdcall Release(void) { + if(--Refs==0) { + RealInput->Release(); + delete this; + return 0; + } else { return Refs; } + } + /*** IDirectInput8A methods ***/ + HRESULT _stdcall CreateDevice(REFGUID r,IDirectInputDeviceA** device,IUnknown* unused) { + GUID GUID_SysMouse = { 0x6F1D2B60, 0xD5A0, 0x11CF, { 0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00} }; + GUID GUID_SysKeyboard = { 0x6F1D2B61, 0xD5A0, 0x11CF, { 0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00} }; + + if(r!=GUID_SysKeyboard&&r!=GUID_SysMouse) { + return RealInput->CreateDevice(r,device,unused); + } else { + DWORD d; + IDirectInputDeviceA* RealDevice; + HRESULT hr; + + if(r==GUID_SysKeyboard) d=kDeviceType_KEYBOARD; + else d=kDeviceType_MOUSE; + hr=RealInput->CreateDevice(r,&RealDevice,unused); + if(hr!=DI_OK) return hr; + + FakeDirectInputDevice* fd=new FakeDirectInputDevice(RealDevice,d); + *device = fd; + if(d==kDeviceType_MOUSE) { + if(FAILED(fd->SetDataFormat(&c_dfDIMouse2))) { + dlogr("ERROR: Could not set mouse format to DIMOUSESTATE2. Only 4 buttons will be available!", DL_MAIN); + } else fd->LockDataFormat(true); + } + return DI_OK; + } + } + HRESULT _stdcall EnumDevices(DWORD a,LPDIENUMDEVICESCALLBACKA b,void* c,DWORD d) { return RealInput->EnumDevices(a,b,c,d); } + HRESULT _stdcall GetDeviceStatus(REFGUID r) { return RealInput->GetDeviceStatus(r); } + HRESULT _stdcall RunControlPanel(HWND a,DWORD b) { return RealInput->RunControlPanel(a,b); } + HRESULT _stdcall Initialize(HINSTANCE a,DWORD b) { return RealInput->Initialize(a,b); } +}; + +HRESULT _stdcall FakeDirectInputCreate(HINSTANCE a,DWORD b,IDirectInputA** c,IUnknown* d) { + ZeroMemory(KeysDown,sizeof(KeysDown)); + + HMODULE dinput=LoadLibraryA("dinput.dll"); + if(!dinput||dinput==INVALID_HANDLE_VALUE) return -1; + DInputCreateProc proc=(DInputCreateProc)GetProcAddress(dinput, "DirectInputCreateA"); + if(!proc) return -1; + + HRESULT hr=proc(a,b,c,d); + if(FAILED(hr)) return hr; + + ReverseMouse=GetPrivateProfileInt("Input", "ReverseMouseButtons", 0, ini)!=0; + + UseScrollWheel=GetPrivateProfileInt("Input", "UseScrollWheel", 1, ini)!=0; + WheelMod=GetPrivateProfileInt("Input", "ScrollMod", 0, ini); + LONG MouseSpeed=GetPrivateProfileInt("Input", "MouseSensitivity", 100, ini); + if(MouseSpeed!=100) { + AdjustMouseSpeed=true; + MouseSpeedMod=((double)MouseSpeed)/100.0; + MousePartX=0; + MousePartY=0; + } else AdjustMouseSpeed=false; + + MiddleMouseKey=GetPrivateProfileInt("Input", "MiddleMouse", 0x30, ini); + MiddleMouseDown=false; + + BackgroundKeyboard=GetPrivateProfileInt("Input", "BackgroundKeyboard", 0, ini)!=0; + BackgroundMouse=GetPrivateProfileInt("Input", "BackgroundMouse", 0, ini)!=0; + + DebugEditorKey=GetPrivateProfileInt("Input", "DebugEditorKey", 0, ini); + + *c=(IDirectInputA*)new FakeDirectInput(*c); + + keyboardLayout = GetKeyboardLayout(0); + + return hr; +} diff --git a/sfall/exports.def b/sfall/exports.def new file mode 100644 index 00000000..52433d71 --- /dev/null +++ b/sfall/exports.def @@ -0,0 +1,6 @@ +LIBRARY ddraw +EXPORTS + +DirectDrawCreate=FakeDirectDrawCreate +DirectDrawCreat2=FakeDirectDrawCreate2 +DirectInputCreateA=FakeDirectInputCreate diff --git a/sfall/heads.cpp b/sfall/heads.cpp new file mode 100644 index 00000000..d235c66a --- /dev/null +++ b/sfall/heads.cpp @@ -0,0 +1,190 @@ +/* + * sfall + * Copyright (C) 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include +#include +#include + +void _stdcall SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff); + +extern IDirect3DDevice9* d3d9Device; +static stdext::hash_map<__int64, IDirect3DTexture9**> texMap; +typedef stdext::hash_map<__int64, IDirect3DTexture9**> :: iterator tex_itr; +typedef stdext::hash_map<__int64, IDirect3DTexture9**> :: const_iterator tex_citr; +static BYTE overridden=0; + +#pragma pack(push, 1) +struct Frm { + DWORD version; + WORD fps; + WORD actionFrame; + WORD frames; + union { + WORD xshifts[6]; + struct { + WORD xshift; + WORD magic; + union { + char path[8]; + __int64 key; + }; + }; + }; + union { + WORD yshifts[6]; + struct { + WORD yshift; + BYTE loaded; + BYTE broken; + IDirect3DTexture9** textures; + BYTE bakedBackground; + BYTE showHighlights; + }; + }; +}; +struct Frame { + WORD width; + WORD height; + DWORD size; + WORD xoffset; + WORD yoffset; + BYTE data[1]; +}; +#pragma pack(pop) + +static const DWORD _frame_pointer=0x419880; +static Frame* FramePointer(const Frm* frm, int frameno) { + Frame* result; + __asm { + mov eax, frm; + mov edx, frameno; + xor ebx, ebx; + call _frame_pointer; + mov result, eax; + } + return result; +} + +static void LoadFrm(Frm* frm) { + tex_citr itr=texMap.find(frm->key); + if(itr==texMap.end()) { + //Load textures + char buf[MAX_PATH], buf2[MAX_PATH]; + strcpy(buf, "data\\art\\heads\\"); + strncat(buf, frm->path, 8); + strcat(buf, "\\%d.png"); + IDirect3DTexture9** textures=new IDirect3DTexture9*[frm->frames]; + for(int i=0;iframes;i++) { + sprintf(buf2, buf, i); + if(FAILED(D3DXCreateTextureFromFileExA(d3d9Device, buf2, 0, 0, 1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, 0, &textures[i]))) { + for(int j=0;jRelease(); + delete[] textures; + frm->broken=1; + return; + } + } + frm->textures=textures; + texMap[frm->key]=textures; + } else { + //Use preloaded textures + frm->textures=itr->second; + } + //mask image + for(int i=0;iframes;i++) { + Frame* frame=FramePointer(frm, i); + if(frm->bakedBackground) { + memset(frame->data, 0x30, frame->size); + } else { + for(DWORD j=0;jsize;j++) { + if(frame->data[j]) frame->data[j]=0x30; + } + } + } + frm->loaded=1; +} + +static void _stdcall DrawFrmHookInternal(Frm* frm, int frameno) { + if(!frm) return; + if(frm->magic==0xabcd&&!frm->broken) { + if(!frm->loaded) LoadFrm(frm); + if(frm->broken) return; + Frame* frame=FramePointer(frm, frameno); + SetHeadTex(frm->textures[frameno], frame->width, frame->height, frame->xoffset+frm->xshift, frame->yoffset+frm->yshift); + overridden=!frm->showHighlights; + } else overridden=0; +} + +static const DWORD gdDisplayFrameRet=0x44AD06; +static void __declspec(naked) DrawFrmHook() { + __asm { + push edx; + push eax; + push edx; + push eax; + call DrawFrmHookInternal; + pop eax; + pop edx; + sub esp, 0x38; + mov esi, eax; + jmp gdDisplayFrameRet; + } +} + +static const DWORD EndSpeechHookRet=0x447299; +void __declspec(naked) EndSpeechHook() { + __asm { + push label; + push ebx; + push ecx; + push edx; + push edi; + push ebp; + jmp EndSpeechHookRet; +label: + xor eax, eax; + push eax; + push eax; + push eax; + push eax; + push eax; + call SetHeadTex; + retn; + } +} + +static const DWORD _talk_to_translucent_trans_buf_to_buf=0x44AC68; +static void __declspec(naked) TransTalkHook() { + __asm { + cmp overridden, 0; + jne skip; + jmp _talk_to_translucent_trans_buf_to_buf; +skip: + retn 0x18; + } +} + +void HeadsInit() { + if(!GetPrivateProfileInt("Graphics", "Use32BitHeadGraphics", 0, ini)) return; + HookCall(0x44AFB4, &TransTalkHook); + HookCall(0x44B00B, &TransTalkHook); + MakeCall(0x44AD01, &DrawFrmHook, true); + MakeCall(0x447294, &EndSpeechHook, true); +} \ No newline at end of file diff --git a/sfall/heads.h b/sfall/heads.h new file mode 100644 index 00000000..b6ae737b --- /dev/null +++ b/sfall/heads.h @@ -0,0 +1 @@ +void HeadsInit(); \ No newline at end of file diff --git a/sfall/input.h b/sfall/input.h new file mode 100644 index 00000000..c250b183 --- /dev/null +++ b/sfall/input.h @@ -0,0 +1,172 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void SetMPMode(bool active); +void SetMDown(bool down, bool right); +void SetMPos(int x, int y); + +DWORD _stdcall KeyDown(DWORD key); +void _stdcall TapKey(DWORD key); + +void GetMouse(int* x, int* y); + +#define DIK_ESCAPE 0x01 +#define DIK_1 0x02 +#define DIK_2 0x03 +#define DIK_3 0x04 +#define DIK_4 0x05 +#define DIK_5 0x06 +#define DIK_6 0x07 +#define DIK_7 0x08 +#define DIK_8 0x09 +#define DIK_9 0x0A +#define DIK_0 0x0B +#define DIK_MINUS 0x0C /* - on main keyboard */ +#define DIK_EQUALS 0x0D +#define DIK_BACK 0x0E /* backspace */ +#define DIK_TAB 0x0F +#define DIK_Q 0x10 +#define DIK_W 0x11 +#define DIK_E 0x12 +#define DIK_R 0x13 +#define DIK_T 0x14 +#define DIK_Y 0x15 +#define DIK_U 0x16 +#define DIK_I 0x17 +#define DIK_O 0x18 +#define DIK_P 0x19 +#define DIK_LBRACKET 0x1A +#define DIK_RBRACKET 0x1B +#define DIK_RETURN 0x1C /* Enter on main keyboard */ +#define DIK_LCONTROL 0x1D +#define DIK_A 0x1E +#define DIK_S 0x1F +#define DIK_D 0x20 +#define DIK_F 0x21 +#define DIK_G 0x22 +#define DIK_H 0x23 +#define DIK_J 0x24 +#define DIK_K 0x25 +#define DIK_L 0x26 +#define DIK_SEMICOLON 0x27 +#define DIK_APOSTROPHE 0x28 +#define DIK_GRAVE 0x29 /* accent grave */ +#define DIK_LSHIFT 0x2A +#define DIK_BACKSLASH 0x2B +#define DIK_Z 0x2C +#define DIK_X 0x2D +#define DIK_C 0x2E +#define DIK_V 0x2F +#define DIK_B 0x30 +#define DIK_N 0x31 +#define DIK_M 0x32 +#define DIK_COMMA 0x33 +#define DIK_PERIOD 0x34 /* . on main keyboard */ +#define DIK_SLASH 0x35 /* / on main keyboard */ +#define DIK_RSHIFT 0x36 +#define DIK_MULTIPLY 0x37 /* * on numeric keypad */ +#define DIK_LMENU 0x38 /* left Alt */ +#define DIK_SPACE 0x39 +#define DIK_CAPITAL 0x3A +#define DIK_F1 0x3B +#define DIK_F2 0x3C +#define DIK_F3 0x3D +#define DIK_F4 0x3E +#define DIK_F5 0x3F +#define DIK_F6 0x40 +#define DIK_F7 0x41 +#define DIK_F8 0x42 +#define DIK_F9 0x43 +#define DIK_F10 0x44 +#define DIK_NUMLOCK 0x45 +#define DIK_SCROLL 0x46 /* Scroll Lock */ +#define DIK_NUMPAD7 0x47 +#define DIK_NUMPAD8 0x48 +#define DIK_NUMPAD9 0x49 +#define DIK_SUBTRACT 0x4A /* - on numeric keypad */ +#define DIK_NUMPAD4 0x4B +#define DIK_NUMPAD5 0x4C +#define DIK_NUMPAD6 0x4D +#define DIK_ADD 0x4E /* + on numeric keypad */ +#define DIK_NUMPAD1 0x4F +#define DIK_NUMPAD2 0x50 +#define DIK_NUMPAD3 0x51 +#define DIK_NUMPAD0 0x52 +#define DIK_DECIMAL 0x53 /* . on numeric keypad */ +#define DIK_F11 0x57 +#define DIK_F12 0x58 + +#define DIK_F13 0x64 /* (NEC PC98) */ +#define DIK_F14 0x65 /* (NEC PC98) */ +#define DIK_F15 0x66 /* (NEC PC98) */ + +#define DIK_KANA 0x70 /* (Japanese keyboard) */ +#define DIK_CONVERT 0x79 /* (Japanese keyboard) */ +#define DIK_NOCONVERT 0x7B /* (Japanese keyboard) */ +#define DIK_YEN 0x7D /* (Japanese keyboard) */ +#define DIK_NUMPADEQUALS 0x8D /* = on numeric keypad (NEC PC98) */ +#define DIK_CIRCUMFLEX 0x90 /* (Japanese keyboard) */ +#define DIK_AT 0x91 /* (NEC PC98) */ +#define DIK_COLON 0x92 /* (NEC PC98) */ +#define DIK_UNDERLINE 0x93 /* (NEC PC98) */ +#define DIK_KANJI 0x94 /* (Japanese keyboard) */ +#define DIK_STOP 0x95 /* (NEC PC98) */ +#define DIK_AX 0x96 /* (Japan AX) */ +#define DIK_UNLABELED 0x97 /* (J3100) */ +#define DIK_NUMPADENTER 0x9C /* Enter on numeric keypad */ +#define DIK_RCONTROL 0x9D +#define DIK_NUMPADCOMMA 0xB3 /* , on numeric keypad (NEC PC98) */ +#define DIK_DIVIDE 0xB5 /* / on numeric keypad */ +#define DIK_SYSRQ 0xB7 +#define DIK_RMENU 0xB8 /* right Alt */ +#define DIK_HOME 0xC7 /* Home on arrow keypad */ +#define DIK_UP 0xC8 /* UpArrow on arrow keypad */ +#define DIK_PRIOR 0xC9 /* PgUp on arrow keypad */ +#define DIK_LEFT 0xCB /* LeftArrow on arrow keypad */ +#define DIK_RIGHT 0xCD /* RightArrow on arrow keypad */ +#define DIK_END 0xCF /* End on arrow keypad */ +#define DIK_DOWN 0xD0 /* DownArrow on arrow keypad */ +#define DIK_NEXT 0xD1 /* PgDn on arrow keypad */ +#define DIK_INSERT 0xD2 /* Insert on arrow keypad */ +#define DIK_DELETE 0xD3 /* Delete on arrow keypad */ +#define DIK_LWIN 0xDB /* Left Windows key */ +#define DIK_RWIN 0xDC /* Right Windows key */ +#define DIK_APPS 0xDD /* AppMenu key */ + +/* + * Alternate names for keys, to facilitate transition from DOS. + */ +#define DIK_BACKSPACE DIK_BACK /* backspace */ +#define DIK_NUMPADSTAR DIK_MULTIPLY /* * on numeric keypad */ +#define DIK_LALT DIK_LMENU /* left Alt */ +#define DIK_CAPSLOCK DIK_CAPITAL /* CapsLock */ +#define DIK_NUMPADMINUS DIK_SUBTRACT /* - on numeric keypad */ +#define DIK_NUMPADPLUS DIK_ADD /* + on numeric keypad */ +#define DIK_NUMPADPERIOD DIK_DECIMAL /* . on numeric keypad */ +#define DIK_NUMPADSLASH DIK_DIVIDE /* / on numeric keypad */ +#define DIK_RALT DIK_RMENU /* right Alt */ +#define DIK_UPARROW DIK_UP /* UpArrow on arrow keypad */ +#define DIK_PGUP DIK_PRIOR /* PgUp on arrow keypad */ +#define DIK_LEFTARROW DIK_LEFT /* LeftArrow on arrow keypad */ +#define DIK_RIGHTARROW DIK_RIGHT /* RightArrow on arrow keypad */ +#define DIK_DOWNARROW DIK_DOWN /* DownArrow on arrow keypad */ +#define DIK_PGDN DIK_NEXT /* PgDn on arrow keypad */ + +void _stdcall ForceGraphicsRefresh(DWORD); diff --git a/sfall/main.cpp b/sfall/main.cpp new file mode 100644 index 00000000..21932439 --- /dev/null +++ b/sfall/main.cpp @@ -0,0 +1,1786 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2011, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include +#include "timer.h" +#include "version.h" +#include "FalloutEngine.h" +#include "Graphics.h" +#include "LoadGameHook.h" +#include "AmmoMod.h" +#include "Logging.h" +#include "ScriptExtender.h" +#include "knockback.h" +#include "perks.h" +#include "KillCounter.h" +#include "Elevators.h" +#include "stats.h" +#include "movies.h" +#include "Criticals.h" +#include "console.h" +#include "FileSystem.h" +#include "HeroAppearance.h" +#include "Credits.h" +#include "Tiles.h" +#include "QuestList.h" +#include "CRC.h" +#include "Premade.h" +#include "sound.h" +#include "Reputations.h" +#include "SuperSave.h" +#include "BarBoxes.h" +#include "AnimationsAtOnceLimit.h" +#include "skills.h" +#include "Inventory.h" +#include "MainMenu.h" +#include "AI.h" +#include "PartyControl.h" +#include "BurstMods.h" +#include "Explosions.h" +#include "Books.h" + +char ini[65]; +char translationIni[65]; + +static char mapName[65]; +static char configName[65]; +static char patchName[65]; +static char versionString[65]; + +static char smModelName[65]; +char dmModelName[65]; +static char sfModelName[65]; +char dfModelName[65]; + +static const char* debugLog="LOG"; +static const char* debugGnw="GNW"; + +static const char* musicOverridePath="data\\sound\\music\\"; + +bool npcautolevel; + +static int* scriptDialog; + +//GetTickCount calls +static const DWORD offsetsA[] = { + 0x004c8d34, + 0x004c9375, + 0x004c9384, + 0x004c93c0, + 0x004c93e8, + 0x004c9d2e, + 0x004fe01e, +}; + +//Delayed GetTickCount calls +static const DWORD offsetsB[] = { + 0x004fdf64, +}; + +//timeGetTime calls +static const DWORD offsetsC[] = { + 0x004a3179, + 0x004a325d, + 0x004f482b, + 0x004f4e53, + 0x004f5542, + 0x004f56cc, + 0x004f59c6, + 0x004fe036, +}; + +static const DWORD origFuncPosA=0x006c0200; +static const DWORD origFuncPosB=0x006c03b0; +static const DWORD origFuncPosC=0x006c0164; + +static const DWORD getLocalTimePos=0x004fdf58; + +static const DWORD dinputPos=0x0050FB70; +static const DWORD DDrawPos=0x0050FB5C; + +static DWORD AddrGetTickCount; +static DWORD AddrGetLocalTime; + +static DWORD ViewportX; +static DWORD ViewportY; + +static __declspec(naked) void GetDateWrapper() { + __asm { + push ecx; + push esi; + push ebx; + mov ecx, 0x004A3338; + call ecx; + mov ecx, ds:[0x51C3BC]; + pop esi; + test esi, esi; + jz end; + add ecx, [esi]; + mov [esi], ecx; +end: + pop esi; + pop ecx; + retn; + } +} +static void TimerReset() { + *((DWORD*)0x51C720)=0; + *((DWORD*)0x51C3BC)+=13; +} + +static double TickFrac=0; +static double MapMulti=1; +static double MapMulti2=1; +void _stdcall SetMapMulti(float d) { MapMulti2=d; } +static __declspec(naked) void PathfinderFix3() { + __asm { + xor eax, eax; + retn; + } +} +static DWORD _stdcall PathfinderFix2(DWORD perkLevel, DWORD ticks) { + double d = MapMulti*MapMulti2; + if(perkLevel==1) d*=0.75; + else if(perkLevel==2) d*=0.5; + else if(perkLevel==3) d*=0.25; + d=((double)ticks)*d + TickFrac; + TickFrac = modf(d, &d); + return (DWORD)d; +} +static __declspec(naked) void PathfinderFix() { + __asm { + push ebx; + push eax; + mov eax, ds:[0x006610B8]; + mov edx, 0x2b; + mov ebx, 0x00496B78; + call ebx; + push eax; + call PathfinderFix2; + mov ebx, 0x004A34CC; + call ebx; + pop ebx; + retn; + } +} + +static double FadeMulti; +static __declspec(naked) void FadeHook() { + __asm { + push ecx; + pushf; + push ebx; + fild [esp]; + fmul FadeMulti; + fistp [esp]; + pop ebx; + popf; + mov ecx, 0x004C7320; + call ecx; + pop ecx; + retn; + } +} + +static int mapSlotsScrollMax=27 * (17 - 7); + +static __declspec(naked) void ScrollCityListHook() { + __asm { + push ebx; + mov ebx, ds:[0x672F10]; + test eax, eax; + jl up; + cmp ebx, mapSlotsScrollMax; + je end; + jmp run; +up: + test ebx, ebx; + jz end; +run: + mov ebx, 0x004C219C; + call ebx; +end: + pop ebx; + retn; + } +} + +static const DWORD wp_function_timer=0x4c9370; +static const DWORD wp_function_difference=0x4c93e0; +static const DWORD wp_function_event=0x4c8b78; +static DWORD wp_delay; +static void __declspec(naked) worldmap_patch() { + __asm { + pushad; + call RunGlobalScripts3; + mov ecx, wp_delay; +tck: + mov eax,ds:[0x50fb08]; + call wp_function_difference; + cmp eax,ecx; + jl tck; + call wp_function_timer; + mov ds:[0x50fb08],eax; + popad; + jmp wp_function_event; + } +} +static const DWORD WorldMapMoveFunc=0x004C1F90; +static void __declspec(naked) WorldMapEncPatch1() { + __asm { + inc dword ptr ds:[0x51dea0] + call WorldMapMoveFunc; + retn; + } +} +static void __declspec(naked) WorldMapEncPatch2() { + __asm { + mov dword ptr ds:[0x51dea0],0; + retn; + } +} +static void __declspec(naked) WorldMapEncPatch3() { + __asm { + mov eax,ds:[0x51dea0]; + retn; + } +} +static const DWORD Combat_p_procFixFunc0=0x4AEF48; //&GetCurrentStat(void* critter, int statID) +static const DWORD Combat_p_procFixFunc1=0x4a3b0c; +static const DWORD Combat_p_procFixFunc2=0x4a3b34; +static const DWORD Combat_p_procFixFunc3=0x4a4810; +static void __declspec(naked) Combat_p_procFix() { + __asm { + push eax; + + mov eax,dword ptr ds:[0x510944]; + cmp eax,3; + jnz end_cppf; + + push esi; + push ebx; + push edx; + + mov esi,0x56d2b0; + mov eax,[esi]; + mov ebx,[esi+0x20]; + xor edx,edx; + mov eax,[eax+0x78]; + call Combat_p_procFixFunc1; + mov eax,[esi]; + + cmp dword ptr ds:[esi+0x2c],+0x0; + jng jmp1; + + test byte ptr ds:[esi+0x15],0x1; + jz jmp1; + mov edx,0x2; + jmp jmp2; +jmp1: + mov edx,0x1; +jmp2: + mov eax,[eax+0x78]; + call Combat_p_procFixFunc2; + mov eax,[esi]; + mov edx,0xd; + mov eax,[eax+0x78]; + call Combat_p_procFixFunc3; + pop edx; + pop ebx; + pop esi; + +end_cppf: + pop eax; + call Combat_p_procFixFunc0; + + retn; + } +} +static double wm_nexttick; +static double wm_wait; +static bool wm_usingperf; +static __int64 wm_perfadd; +static __int64 wm_perfnext; +static DWORD WorldMapLoopCount; +static const DWORD WorldMapRealFunc=0x004C8B78; +static void WorldMapSpeedPatch3() { + RunGlobalScripts3(); + if(wm_usingperf) { + __int64 timer; + while(true) { + QueryPerformanceCounter((LARGE_INTEGER*)&timer); + if(timer>wm_perfnext) break; + Sleep(0); + } + if(wm_perfnext+wm_perfadd < timer) wm_perfnext = timer+wm_perfadd; + else wm_perfnext+=wm_perfadd; + } else { + DWORD tick; + DWORD nexttick=(DWORD)wm_nexttick; + while((tick=GetTickCount())cFileName)>12) { + int i=FindNextFileA(h, data); + if(i==0) { + FindClose(h); + return INVALID_HANDLE_VALUE; + } + } + return h; +} +int _stdcall FakeFindNextFile(HANDLE h, WIN32_FIND_DATAA* data) { + int i=FindNextFileA(h, data); + while(strlen(data->cFileName)>12&&i) { + i=FindNextFileA(h, data); + } + return i; +} +static const DWORD WeaponAnimAddr=0x419314; +static void __declspec(naked) WeaponAnimHook() { + __asm { + cmp edx, 11; + je c11; + cmp edx, 15; + je c15; + jmp WeaponAnimAddr; +c11: + mov edx, 16; + jmp WeaponAnimAddr; +c15: + mov edx, 17; + jmp WeaponAnimAddr; + } +} +static const DWORD SetGlobalVarAddr=0x443C98; +static const DWORD GetGlobalVarAddr=0x443C68; +static char KarmaGainMsg[128]; +static char KarmaLossMsg[128]; +static void _stdcall SetKarma(int value) { + int old; + __asm { + xor eax, eax; + call GetGlobalVarAddr; + mov old, eax; + } + old=value-old; + char buf[64]; + if(old==0) return; + if(old>0) { + sprintf_s(buf, KarmaGainMsg, old); + } else { + sprintf_s(buf, KarmaLossMsg, -old); + } + DisplayConsoleMessage(buf); +} +static void __declspec(naked) SetGlobalVarWrapper() { + __asm { + test eax, eax; + jnz end; + pushad; + push edx; + call SetKarma; + popad; +end: + jmp SetGlobalVarAddr; + } +} +static const DWORD PlaySfxInternal=0x4519A8; +static const DWORD AnimClear=0x413C4C; +static const DWORD AnimBegin=0x413AF4; +static const DWORD AnimEnd=0x413CCC; +static const DWORD RegAnim=0x4149D0; +static void __declspec(naked) ReloadHook() { + __asm { + push eax; + push ebx; + push edx; + mov eax, dword ptr ds:[0x6610B8]; + call AnimClear; + xor eax, eax; + inc eax; + call AnimBegin; + xor edx, edx; + xor ebx, ebx; + mov eax, dword ptr ds:[0x6610B8]; + dec ebx; + call RegAnim; + call AnimEnd; + pop edx; + pop ebx; + pop eax; + jmp PlaySfxInternal; + } +} +static const DWORD CorpseHitFix2_continue_loop1 = 0x48B99B; +static void __declspec(naked) CorpseHitFix2() { + __asm { + push eax; + mov eax, [eax]; + call critter_is_dead_; // found some object, check if it's a dead critter + test eax, eax; + pop eax; + jz really_end; // if not, allow breaking the loop (will return this object) + jmp CorpseHitFix2_continue_loop1; // otherwise continue searching + +really_end: + mov eax, [eax]; + pop ebp; + pop edi; + pop esi; + pop ecx; + retn; + } +} +static const DWORD CorpseHitFix2_continue_loop2 = 0x48BA0B; +// same logic as above, for different loop +static void __declspec(naked) CorpseHitFix2b() { + __asm { + mov eax, [edx]; + call critter_is_dead_; + test eax, eax; + jz really_end; + jmp CorpseHitFix2_continue_loop2; + +really_end: + mov eax, [edx]; + pop ebp; + pop edi; + pop esi; + pop ecx; + retn; + } +} + +static const DWORD _combat_ai=0x42B130; +static const DWORD _process_bk=0x4C8BDC; +static const DWORD RetryCombatRet=0x422BA9; +static DWORD RetryCombatLastAP; +static DWORD RetryCombatMinAP; +static void __declspec(naked) RetryCombatHook() { + __asm { + mov RetryCombatLastAP, 0; +retry: + mov eax, esi; + xor edx, edx; + call _combat_ai; +process: + cmp dword ptr ds:[0x51093C], 0; + jle next; + call _process_bk; + jmp process; +next: + mov eax, [esi+0x40]; + cmp eax, RetryCombatMinAP; + jl end; + cmp eax, RetryCombatLastAP; + je end; + mov RetryCombatLastAP, eax; + jmp retry; +end: + retn; + } +} +static const DWORD IntfaceRotateNumbersRet=0x460BA0; +static void __declspec(naked) IntfaceRotateNumbersHook() { + __asm { + cmp ebx, ecx; + je end; + jg greater; + mov ebx, ecx; + inc ebx; + jmp end; +greater: + mov ebx, ecx; + dec ebx; +end: + jmp IntfaceRotateNumbersRet; + } +} + +static DWORD KarmaFrmCount; +static DWORD* KarmaFrms; +static int* KarmaPoints; +static const DWORD DrawCardAddr=0x43AAEC; +static DWORD _stdcall DrawCardHook2() { + int rep=**(int**)0x5186C0; + for(DWORD i=0;iFID + and ebx, 0x0F000000 + sar ebx, 0x18 + cmp ebx, 1 //check if object FID type flag is set to critter + jne EndFunc //if object not a critter leave jump condition flags set to skip dodge animation + test byte ptr ds:[eax+0x44], 0x03//(original code) check some flag? + EndFunc: + ret + } +} + +static const DWORD ScannerHookRet=0x41BC1D; +static const DWORD ScannerHookFail=0x41BC65; +static const DWORD _inven_pid_is_carried_ptr=0x471CA0; +static void __declspec(naked) ScannerAutomapHook() { + __asm { + mov eax, ds:[0x6610B8]; + mov edx, 59; + call _inven_pid_is_carried_ptr; + test eax, eax; + jz fail; + mov edx, eax; + jmp ScannerHookRet; +fail: + jmp ScannerHookFail; + } +} + +static const DWORD obj_find_first_at_tile=0x48B5A8; +static const DWORD obj_find_next_at_tile=0x48B608; +static void _stdcall explosion_crash_fix_hook2() { + if(InCombat()) return; + for(int elv=0;elv<3;elv++) { + for(int tile=0;tile<40000;tile++) { + DWORD* obj; + __asm { + mov edx, tile; + mov eax, elv; + call obj_find_first_at_tile; + mov obj, eax; + } + while(obj) { + DWORD otype = obj[25]; + otype = (otype&0xff000000) >> 24; + if(otype==1) { + obj[0x12]=0; + obj[0x15]=0; + obj[0x10]=0; + } + __asm { + call obj_find_next_at_tile; + mov obj, eax; + } + } + } + } +} +static const DWORD realfunc=0x413144; +static void __declspec(naked) explosion_crash_fix_hook() { + __asm { + pushad; + call explosion_crash_fix_hook2; + popad; + jmp realfunc; + } +} + +static const DWORD lineOfSightSearch_func = 0x4163C8; +///static const DWORD checkHexObjsIsBlocker_func=0x48B848; //(EAX *obj, EDX hexNum, EBX level) +static const DWORD checkHexObjsIsBlockerShootThru_func = 0x48B930; //(EAX *obj, EDX hexNum, EBX level) +static void __declspec(naked) objCanSeeObj_ShootThru_Fix() {//(EAX *objStruct, EDX hexNum1, EBX hexNum2, ECX ?, stack1 **ret_objStruct, stack2 flags) + __asm { + push esi + push edi + + push checkHexObjsIsBlockerShootThru_func //arg3 check hex objects func pointer + mov esi, 0x20//arg2 flags, 0x20 = check shootthru + push esi + mov edi, dword ptr ss : [esp + 0x14] //arg1 **ret_objStruct + push edi + call lineOfSightSearch_func;//(EAX *objStruct, EDX hexNum1, EBX hexNum2, ECX ?, stack1 **ret_objStruct, stack2 flags, stack3 *check_hex_objs_func) + + pop edi + pop esi + ret 0x8 + } +} + + +static void DllMain2() { + //SafeWrite8(0x4B15E8, 0xc3); + //SafeWrite8(0x4B30C4, 0xc3); //this is the one I need to override for bigger tiles + DWORD tmp; + dlogr("In DllMain2", DL_MAIN); + + //BlockCall(0x4123BC); + + if(GetPrivateProfileIntA("Speed", "Enable", 0, ini)) { + dlog("Applying speed patch.", DL_INIT); + AddrGetTickCount = (DWORD)&FakeGetTickCount; + AddrGetLocalTime = (DWORD)&FakeGetLocalTime; + + for(int i=0;i + SafeWrite16(0x004c0677, 0xE890); //nop, call relative. Replaces 'mov TicksSinceLastEncounter, ecx' + SafeWrite32(0x004c0679, ((DWORD)&WorldMapEncPatch2) - 0x004c067D); + SafeWrite8(0x004c232d, 0xb8); //'mov eax, 0', replaces 'mov eax, GetTickCount()' + SafeWrite32(0x004c232e, 0); + dlogr(" Done", DL_INIT); + } + } + + if(GetPrivateProfileIntA("Misc", "DialogueFix", 1, ini)) { + dlog("Applying dialogue patch.", DL_INIT); + SafeWrite8(0x00446848, 0x31); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "ExtraKillTypes", 0, ini)) { + dlog("Applying extra kill types patch.", DL_INIT); + KillCounterInit(true); + dlogr(" Done", DL_INIT); + } else KillCounterInit(false); + + //if(GetPrivateProfileIntA("Misc", "ScriptExtender", 0, ini)) { + dlog("Applying script extender patch.", DL_INIT); + StatsInit(); + dlog(".", DL_INIT); + ScriptExtenderSetup(); + dlog(".", DL_INIT); + LoadGameHookInit(); + dlog(".", DL_INIT); + PerksInit(); + dlog(".", DL_INIT); + KnockbackInit(); + dlog(".", DL_INIT); + SkillsInit(); + dlog(".", DL_INIT); + + //Ray's combat_p_proc fix + SafeWrite32(0x0425253, ((DWORD)&Combat_p_procFix) - 0x0425257); + SafeWrite8(0x0424dbc, 0xE9); + SafeWrite32(0x0424dbd, 0x00000034); + dlogr(" Done", DL_INIT); + //} + + //if(GetPrivateProfileIntA("Misc", "WorldMapCitiesListFix", 0, ini)) { + dlog("Applying world map cities list patch.", DL_INIT); + + SafeWrite32(0x004C04BA, ((DWORD)&ScrollCityListHook) - 0x004C04BE); + SafeWrite32(0x004C04C9, ((DWORD)&ScrollCityListHook) - 0x004C04CD); + SafeWrite32(0x004C4A35, ((DWORD)&ScrollCityListHook) - 0x004C4A39); + SafeWrite32(0x004C4A3E, ((DWORD)&ScrollCityListHook) - 0x004C4A42); + dlogr(" Done", DL_INIT); + //} + + //if(GetPrivateProfileIntA("Misc", "CitiesLimitFix", 0, ini)) { + dlog("Applying cities limit patch.", DL_INIT); + if(*((BYTE*)0x004BF3BB)!=0xeb) { + SafeWrite8(0x004BF3BB, 0xeb); + } + dlogr(" Done", DL_INIT); + //} + + tmp=GetPrivateProfileIntA("Misc", "WorldMapSlots", 0, ini); + if(tmp&&tmp<128) { + dlog("Applying world map slots patch.", DL_INIT); + if(tmp<7) tmp=7; + mapSlotsScrollMax = (tmp-7)*27; + if(tmp<25) SafeWrite32(0x004C21FD, 230 - (tmp-17)*27); + else { + SafeWrite8(0x004C21FC, 0xC2); + SafeWrite32(0x004C21FD, 2 + 27*(tmp-26)); + } + dlogr(" Done", DL_INIT); + } + + int limit=GetPrivateProfileIntA("Misc", "TimeLimit", 13, ini); + if(limit==-2) limit=14; + if(limit==-3) { + dlog("Applying time limit patch (-3).", DL_INIT); + limit=-1; + AddUnarmedStatToGetYear=1; + + SafeWrite32(0x004392F9, ((DWORD)&GetDateWrapper) - 0x004392Fd); + SafeWrite32(0x00443809, ((DWORD)&GetDateWrapper) - 0x0044380d); + SafeWrite32(0x0047E128, ((DWORD)&GetDateWrapper) - 0x0047E12c); + SafeWrite32(0x004975A3, ((DWORD)&GetDateWrapper) - 0x004975A7); + SafeWrite32(0x00497713, ((DWORD)&GetDateWrapper) - 0x00497717); + SafeWrite32(0x004979Ca, ((DWORD)&GetDateWrapper) - 0x004979Ce); + SafeWrite32(0x004C3CB6, ((DWORD)&GetDateWrapper) - 0x004C3CBa); + dlogr(" Done", DL_INIT); + } + if(limit<=14&&limit>=-1&&limit!=13) { + dlog("Applying time limit patch.", DL_INIT); + if(limit==-1) { + SafeWrite32(0x004A34Fa, ((DWORD)&TimerReset) - 0x004A34Fe); + SafeWrite32(0x004A3552, ((DWORD)&TimerReset) - 0x004A3556); + + SafeWrite32(0x004A34EF, 0x90909090); + SafeWrite32(0x004A34f3, 0x90909090); + SafeWrite16(0x004A34f7, 0x9090); + SafeWrite32(0x004A34FE, 0x90909090); + SafeWrite16(0x004A3502, 0x9090); + + SafeWrite32(0x004A3547, 0x90909090); + SafeWrite32(0x004A354b, 0x90909090); + SafeWrite16(0x004A354f, 0x9090); + SafeWrite32(0x004A3556, 0x90909090); + SafeWrite16(0x004A355a, 0x9090); + } else { + SafeWrite8(0x004A34EC, limit); + SafeWrite8(0x004A3544, limit); + } + dlogr(" Done", DL_INIT); + } + +#ifdef TRACE + tmp=GetPrivateProfileIntA("Debugging", "DebugMode", 0, ini); + if(tmp) { + dlog("Applying debugmode patch.", DL_INIT); + //If the player is using an exe with the debug patch already applied, just skip this block without erroring + if(*((DWORD*)0x00444A64)!=0x082327e8) { + SafeWrite32(0x00444A64, 0x082327e8); + SafeWrite32(0x00444A68, 0x0120e900); + SafeWrite8(0x00444A6D, 0); + SafeWrite32(0x00444A6E, 0x90909090); + } + SafeWrite8(0x004C6D9B, 0xb8); + if(tmp==1) SafeWrite32(0x004C6D9C, (DWORD)debugGnw); + else SafeWrite32(0x004C6D9C, (DWORD)debugLog); + dlogr(" Done", DL_INIT); + } +#endif + + npcautolevel=GetPrivateProfileIntA("Misc", "NPCAutoLevel", 0, ini)!=0; + if(npcautolevel) { + dlog("Applying npc autolevel patch.", DL_INIT); + SafeWrite16(0x00495D22, 0x9090); + SafeWrite32(0x00495D24, 0x90909090); + dlogr(" Done", DL_INIT); + } + //if(GetPrivateProfileIntA("Misc", "NPCLevelFix", 0, ini)) { + dlog("Applying NPCLevelFix.", DL_INIT); + HookCall(0x495BC9, (void*)0x495E51); + dlogr(" Done", DL_INIT); + //} + + if(GetPrivateProfileIntA("Misc", "SingleCore", 1, ini)) { + dlog("Applying single core patch.", DL_INIT); + HANDLE process=GetCurrentProcess(); + SetProcessAffinityMask(process, 1); + CloseHandle(process); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "OverrideArtCacheSize", 0, ini)) { + dlog("Applying override art cache size patch.", DL_INIT); + SafeWrite32(0x00418867, 0x90909090); + SafeWrite32(0x00418872, 256); + dlogr(" Done", DL_INIT); + } + + char elevPath[MAX_PATH]; + GetPrivateProfileString("Misc", "ElevatorsFile", "", elevPath, MAX_PATH, ini); + if(strlen(elevPath)>0) { + dlog("Applying elevator patch.", DL_INIT); + ElevatorsInit(elevPath); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "UseFileSystemOverride", 0, ini)) { + FileSystemInit(); + } + + /*DWORD horrigan=GetPrivateProfileIntA("Misc", "DisableHorrigan", 0, ini); + if(horrigan) { + if(*((BYTE*)0x004C06D8)!=0x75) return false; + SafeWrite8(0x004C06D8, 0xeb); + }*/ + + if(GetPrivateProfileIntA("Misc", "BlackSkilldexFix", 1, ini)) { + dlog("Applying black skilldex patch.", DL_INIT); + HookCall(0x00497D0F, BlackSkilldexFix); + dlogr(" Done", DL_INIT); + } + + //if(GetPrivateProfileIntA("Misc", "PrintToFileFix", 0, ini)) { + dlog("Applying print to file patch.", DL_INIT); + SafeWrite32(0x006C0364, (DWORD)&FakeFindFirstFile); + SafeWrite32(0x006C0368, (DWORD)&FakeFindNextFile); + dlogr(" Done", DL_INIT); + //} + + if(GetPrivateProfileIntA("Misc", "AdditionalWeaponAnims", 0, ini)) { + dlog("Applying additional weapon animations patch.", DL_INIT); + SafeWrite8(0x419320, 0x12); + HookCall(0x4194CC, WeaponAnimHook); + HookCall(0x451648, WeaponAnimHook); + HookCall(0x451671, WeaponAnimHook); + dlogr(" Done", DL_INIT); + } + +#ifdef TRACE + if(GetPrivateProfileIntA("Debugging", "DontDeleteProtos", 0, ini)) { + dlog("Applying permanent protos patch.", DL_INIT); + SafeWrite8(0x48007E, 0xeb); + dlogr(" Done", DL_INIT); + } +#endif + + //if(GetPrivateProfileIntA("Misc", "FixWithdrawalPerkDescCrash", 0, ini)) { + dlog("Applying withdrawal perk description crash fix. ", DL_INIT); + HookCall(0x47A501, AlcoholFix); + dlogr(" Done", DL_INIT); + //} + + CritInit(); + + int number_patch_loop=GetPrivateProfileInt("Misc", "NumberPatchLoop", -1, ini); + if(number_patch_loop>-1) { + dlog("Applying load multiple patches patch. ", DL_INIT); + // Disable check + SafeWrite8(0x0444363, 0xE9); + SafeWrite32(0x0444364, 0xFFFFFFB9); + // New loop count + SafeWrite32(0x0444357, number_patch_loop); + // Change step from 2 to 1 + SafeWrite8(0x0444354, 0x90); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileInt("Misc", "DisplayKarmaChanges", 0, ini)) { + dlog("Applying display karma changes patch. ", DL_INIT); + GetPrivateProfileString("sfall", "KarmaGain", "You gained %d karma.", KarmaGainMsg, 128, translationIni); + GetPrivateProfileString("sfall", "KarmaLoss", "You lost %d karma.", KarmaLossMsg, 128, translationIni); + HookCall(0x455A6D, SetGlobalVarWrapper); + dlogr(" Done", DL_INIT); + } + + //if(GetPrivateProfileInt("Misc", "ShivPatch", 0, ini)) { + dlog("Applying shiv patch. ", DL_INIT); + SafeWrite8(0x477B2B, 0xeb); + dlogr(" Done", DL_INIT); + //} + + //if(GetPrivateProfileInt("Misc", "ImportedProcedureFix", 0, ini)) { + dlog("Applying imported procedure patch. ", DL_INIT); + SafeWrite16(0x46B35B, 0x1c60); + SafeWrite32(0x46B35D, 0x90909090); + SafeWrite8(0x46DBF1, 0xeb); + SafeWrite8(0x46DDC4, 0xeb); + SafeWrite8(0x4415CC, 0x00); + dlogr(" Done", DL_INIT); + //} + + if(GetPrivateProfileInt("Misc", "AlwaysReloadMsgs", 0, ini)) { + dlog("Applying always reload messages patch. ", DL_INIT); + SafeWrite8(0x4A6B8A, 0xff); + SafeWrite32(0x4A6B8B, 0x02eb0074); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileInt("Misc", "PlayIdleAnimOnReload", 0, ini)) { + dlog("Applying idle anim on reload patch. ", DL_INIT); + HookCall(0x460B8C, ReloadHook); + dlogr(" Done", DL_INIT); + } + + if (GetPrivateProfileInt("Misc", "CorpseLineOfFireFix", 0, ini)) { + dlog("Applying corpse line of fire patch. ", DL_INIT); + + MakeCall(0x48B994, CorpseHitFix2, true); + MakeCall(0x48BA04, CorpseHitFix2b, true); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "EnableHeroAppearanceMod", 0, ini)) { + dlog("Setting up Appearance Char Screen buttons. ", DL_INIT); + EnableHeroAppearanceMod(); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "SkipOpeningMovies", 0, ini)) { + dlog("Blocking opening movies. ", DL_INIT); + BlockCall(0x4809CB); + BlockCall(0x4809D4); + BlockCall(0x4809E0); + dlogr(" Done", DL_INIT); + } + + RetryCombatMinAP=GetPrivateProfileIntA("Misc", "NPCsTryToSpendExtraAP", 0, ini); + if(RetryCombatMinAP) { + dlog("Applying retry combat patch. ", DL_INIT); + HookCall(0x422B94, &RetryCombatHook); + dlogr(" Done", DL_INIT); + } + + dlog("Checking for changed skilldex images. ", DL_INIT); + tmp=GetPrivateProfileIntA("Misc", "Lockpick", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D54, tmp); + tmp=GetPrivateProfileIntA("Misc", "Steal", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D58, tmp); + tmp=GetPrivateProfileIntA("Misc", "Traps", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D5C, tmp); + tmp=GetPrivateProfileIntA("Misc", "FirstAid", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D4C, tmp); + tmp=GetPrivateProfileIntA("Misc", "Doctor", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D50, tmp); + tmp=GetPrivateProfileIntA("Misc", "Science", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D60, tmp); + tmp=GetPrivateProfileIntA("Misc", "Repair", 293, ini); + if(tmp!=293) SafeWrite32(0x00518D64, tmp); + dlogr(" Done", DL_INIT); + + if(GetPrivateProfileIntA("Misc", "RemoveWindowRounding", 0, ini)) { + SafeWrite32(0x4B8090, 0x90909090); + SafeWrite16(0x4B8094, 0x9090); + } + + dlogr("Running TilesInit().", DL_INIT); + TilesInit(); + + dlogr("Applying main menu text patch", DL_INIT); + CreditsInit(); + + if(GetPrivateProfileIntA("Misc", "UseScrollingQuestsList", 0, ini)) { + dlog("Applying quests list patch ", DL_INIT); + QuestListInit(); + dlogr(" Done", DL_INIT); + } + + dlog("Applying premade characters patch", DL_INIT); + PremadeInit(); + + dlogr("Running SoundInit().", DL_INIT); + SoundInit(); + + dlogr("Running ReputationsInit().", DL_INIT); + ReputationsInit(); + + dlogr("Running ConsoleInit().", DL_INIT); + ConsoleInit(); + + if(GetPrivateProfileIntA("Misc", "ExtraSaveSlots", 0, ini)) { + dlog("Running EnableSuperSaving()", DL_INIT); + EnableSuperSaving(); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "SpeedInterfaceCounterAnims", 0, ini)) { + dlog("Applying SpeedInterfaceCounterAnims patch.", DL_INIT); + HookCall(0x45ED63, IntfaceRotateNumbersHook); + HookCall(0x45ED86, IntfaceRotateNumbersHook); + HookCall(0x45EDFA, IntfaceRotateNumbersHook); + dlogr(" Done", DL_INIT); + } + + KarmaFrmCount=GetPrivateProfileIntA("Misc", "KarmaFRMsCount", 0, ini); + if(KarmaFrmCount) { + KarmaFrms=new DWORD[KarmaFrmCount]; + KarmaPoints=new int[KarmaFrmCount-1]; + dlog("Applying karma frm patch.", DL_INIT); + char buf[512]; + GetPrivateProfileStringA("Misc", "KarmaFRMs", "", buf, 512, ini); + char *ptr=buf, *ptr2; + for(DWORD i=0;i32) { + dlog("Applying AnimationsAtOnceLimit patch.", DL_INIT); + AnimationsAtOnceInit((signed char)tmp); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "WieldObjCritterFix", 1, ini)) { + dlog("Applying wield_obj_critter fix.", DL_INIT); + MakeCall(0x45690F, &WieldObjCritterFix, true); + HookCall(0x45697F, &WieldObjCritterFix2); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "JetAntidoteFix", 1, ini)) { + dlog("Applying Jet Antidote fix.", DL_INIT); + MakeCall(0x47A015, &JetAntidoteFix, true); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "RemoveCriticalTimelimits", 0, ini)) { + dlog("Removing critical time limits.", DL_INIT); + SafeWrite8(0x42412B, 0x90); + BlockCall(0x42412C); + SafeWrite16(0x4A3052, 0x9090); + SafeWrite16(0x4A3093, 0x9090); + dlogr(" Done", DL_INIT); + } + + if(tmp=GetPrivateProfileIntA("Sound", "OverrideMusicDir", 0, ini)) { + SafeWrite32(0x4449C2, (DWORD)&musicOverridePath); + SafeWrite32(0x4449DB, (DWORD)&musicOverridePath); + if(tmp==2) { + SafeWrite32(0x518E78, (DWORD)&musicOverridePath); + SafeWrite32(0x518E7C, (DWORD)&musicOverridePath); + } + } + + if(GetPrivateProfileIntA("Misc", "NPCStage6Fix", 0, ini)) { + dlog("Applying NPC Stage 6 Fix.", DL_INIT); + MakeCall(0x493CE9, &NPCStage6Fix1, true); + SafeWrite8(0x494063, 0x06); // loop should look for a potential 6th stage + SafeWrite8(0x4940BB, 0xCC); // move pointer by 204 bytes instead of 200 + MakeCall(0x494224, &NPCStage6Fix2, true); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "MultiHexPathingFix", 1, ini)) { + dlog("Applying MultiHex Pathing Fix.", DL_INIT); + MakeCall(0x42901F, &MultiHexFix1, true); + MakeCall(0x429170, &MultiHexFix2, true); + dlogr(" Done", DL_INIT); + } + + switch(GetPrivateProfileIntA("Misc", "FastShotFix", 1, ini)) { + case 1: + dlog("Applying Fast Shot Trait Fix.", DL_INIT); + MakeCall(0x478E75, &FastShotTraitFix, true); + dlogr(" Done", DL_INIT); + break; + case 2: + dlog("Applying Fast Shot Trait Fix. (Fallout 1 version)", DL_INIT); + SafeWrite16(0x478C9F, 0x9090); +#define hook(a) SafeWrite32(a+1, 0x478C7D - (a+5)) + hook(0x478C2f); + hook(0x478C08); + hook(0x478BF9); + hook(0x478BEA); + hook(0x478BD6); + hook(0x478BC7); + hook(0x478BB8); +#undef hook + dlogr(" Done", DL_INIT); + break; + } + + if(GetPrivateProfileIntA("Misc", "DodgyDoorsFix", 1, ini)) { + dlog("Applying Dodgy Door Fix.", DL_INIT); + SafeWrite16(0x4113D3, 0x9090); + MakeCall(0x4113D5, &DodgyDoorsFix, false); + dlogr(" Done", DL_INIT); + } + + if(GetPrivateProfileIntA("Misc", "BoostScriptDialogLimit", 0, ini)) { + const int scriptDialogCount=10000; + dlog("Applying script dialog limit patch.", DL_INIT); + scriptDialog=new int[scriptDialogCount*2]; //Because the msg structure is 8 bytes, not 4. + //scr_init + SafeWrite32(0x4A50C2, (DWORD)scriptDialog); + SafeWrite32(0x4A50E3, scriptDialogCount); + //scr_game_init + SafeWrite32(0x4A5169, (DWORD)scriptDialog); + SafeWrite32(0x4A519F, scriptDialogCount); + //scr_message_free + SafeWrite32(0x4A52FA, (DWORD)scriptDialog); + SafeWrite32(0x4A5302, (DWORD)scriptDialog); + SafeWrite32(0x4A534F, scriptDialogCount*8); + //scr_get_dialog_msg_file + SafeWrite32(0x4A6B86, (DWORD)scriptDialog); + SafeWrite32(0x4A6BE0, (DWORD)scriptDialog); + SafeWrite32(0x4A6C37, (DWORD)scriptDialog); + dlogr(" Done", DL_INIT); + } + + dlog("Running InventoryInit.", DL_INIT); + InventoryInit(); + dlogr(" Done", DL_INIT); + + if(tmp=GetPrivateProfileIntA("Misc", "MotionScannerFlags", 1, ini)) { + dlog("Applying MotionScannerFlags patch.", DL_INIT); + if(tmp&1) MakeCall(0x41BBE9, &ScannerAutomapHook, true); + if(tmp&2) BlockCall(0x41BC3C); + dlogr(" Done", DL_INIT); + } + + if(tmp=GetPrivateProfileIntA("Misc", "EncounterTableSize", 0, ini) && tmp<=127) { + dlog("Applying EncounterTableSize patch.", DL_INIT); + DWORD nsize=(tmp+1)*180+0x50; + SafeWrite32(0x4BD1A3, nsize); + SafeWrite32(0x4BD1D9, nsize); + SafeWrite32(0x4BD270, nsize); + SafeWrite32(0x4BD604, nsize); + SafeWrite32(0x4BDA14, nsize); + SafeWrite32(0x4BDA44, nsize); + SafeWrite32(0x4BE707, nsize); + SafeWrite32(0x4C0815, nsize); + SafeWrite32(0x4C0D4a, nsize); + SafeWrite32(0x4C0FD4, nsize); + SafeWrite8(0x4BDB17, (BYTE)tmp); + dlogr(" Done", DL_INIT); + } + + dlog("Initing main menu patches.", DL_INIT); + MainMenuInit(); + dlogr(" Done", DL_INIT); + + if(GetPrivateProfileIntA("Misc", "DisablePipboyAlarm", 0, ini)) { + SafeWrite8(0x499518, 0xc3); + } + + dlog("Initing AI patches.", DL_INIT); + AIInit(); + dlogr(" Done", DL_INIT); + + dlog("Initing AI control.", DL_INIT); + PartyControlInit(); + dlogr(" Done", DL_INIT); + + HookCall(0x413105, explosion_crash_fix_hook);//test for explosives + SafeWrite32(0x413034, (DWORD)&explosion_crash_fix_hook); + + if (GetPrivateProfileIntA("Misc", "ObjCanSeeObj_ShootThru_Fix", 0, ini)) { + dlog("Applying ObjCanSeeObj ShootThru Fix.", DL_INIT); + SafeWrite32(0x456BC7, (DWORD)&objCanSeeObj_ShootThru_Fix - 0x456BCB); + dlogr(" Done", DL_INIT); + } + + // phobos2077: + ComputeSprayModInit(); + ExplosionLightingInit(); + tmp = SimplePatch(0x4A2873, "Misc", "Dynamite_DmgMax", 50, 0, 9999); + SimplePatch(0x4A2878, "Misc", "Dynamite_DmgMin", 30, 0, tmp); + tmp = SimplePatch(0x4A287F, "Misc", "PlasticExplosive_DmgMax", 80, 0, 9999); + SimplePatch(0x4A2884, "Misc", "PlasticExplosive_DmgMin", 40, 0, tmp); + BooksInit(); + DWORD addrs[2] = {0x45F9DE, 0x45FB33}; + SimplePatch(addrs, 2, "Misc", "CombatPanelAnimDelay", 1000, 0, 65535); + addrs[0] = 0x447DF4; addrs[1] = 0x447EB6; + SimplePatch(addrs, 2, "Misc", "DialogPanelAnimDelay", 33, 0, 255); + dlogr("Leave DllMain2", DL_MAIN); +} + +static void _stdcall OnExit() { + ConsoleExit(); + AnimationsAtOnceExit(); + HeroAppearanceModExit(); + //SoundExit(); +} + +static const DWORD OnExitRet=0x4E3D3C; +static void __declspec(naked) OnExitFunc() { + __asm { + pushad; + call OnExit; + popad; + jmp OnExitRet; + } +} + +static void CompatModeCheck(HKEY root, const char* filepath, int extra) { + HKEY key; + char buf[MAX_PATH]; + DWORD size=MAX_PATH; + DWORD type; + if(!(type=RegOpenKeyEx(root, "Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", 0, extra|STANDARD_RIGHTS_READ|KEY_QUERY_VALUE, &key))) { + if(!RegQueryValueEx(key, filepath, 0, &type, (BYTE*)buf, &size)) { + if(size&&(type==REG_EXPAND_SZ||type==REG_MULTI_SZ||type==REG_SZ)) { + if(strstr(buf, "256COLOR")||strstr(buf, "640X480")||strstr(buf, "WIN")) { + RegCloseKey(key); + /*if(!RegOpenKeyEx(root, "Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", 0, extra|KEY_READ|KEY_WRITE, &key)) { + if((type=RegDeleteValueA(key, filepath))==ERROR_SUCCESS) { + MessageBoxA(0, "Fallout was set to run in compatibility mode.\n" + "Please restart fallout to ensure it runs correctly.", "Error", 0); + RegCloseKey(key); + ExitProcess(-1); + } else { + //FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, type, 0, buf, 260, 0); + //MessageBoxA(0, buf, "", 0); + } + }*/ + + MessageBoxA(0, "Fallout appears to be running in compatibility mode.\n" //, and sfall was not able to disable it.\n" + "Please check the compatibility tab of fallout2.exe, and ensure that the following settings are unchecked.\n" + "Run this program in compatibility mode for..., run in 256 colours, and run in 640x480 resolution.\n" + "If these options are disabled, click the 'change settings for all users' button and see if that enables them.", "Error", 0); + //RegCloseKey(key); + ExitProcess(-1); + } + } + } + RegCloseKey(key); + } else { + //FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, type, 0, buf, 260, 0); + //MessageBoxA(0, buf, "", 0); + } +} + +bool _stdcall DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved) { + if(dwReason==DLL_PROCESS_ATTACH) { +#ifdef TRACE + LoggingInit(); +#endif + + HookCall(0x4DE7D2, &OnExitFunc); + + char filepath[MAX_PATH]; + GetModuleFileName(0, filepath, MAX_PATH); + + CRC(filepath); + +#ifdef TRACE + if(!GetPrivateProfileIntA("Debugging", "SkipCompatModeCheck", 0, ".\\ddraw.ini")) { +#else + if(1) { +#endif + int is64bit; + typedef int (_stdcall *chk64bitproc)(HANDLE, int*); + HMODULE h=LoadLibrary("Kernel32.dll"); + chk64bitproc proc = (chk64bitproc)GetProcAddress(h, "IsWow64Process"); + if(proc) proc(GetCurrentProcess(), &is64bit); + else is64bit=0; + FreeLibrary(h); + + CompatModeCheck(HKEY_CURRENT_USER, filepath, is64bit?KEY_WOW64_64KEY:0); + CompatModeCheck(HKEY_LOCAL_MACHINE, filepath, is64bit?KEY_WOW64_64KEY:0); + } + + + bool cmdlineexists=false; + char* cmdline=GetCommandLineA(); + if(GetPrivateProfileIntA("Main", "UseCommandLine", 0, ".\\ddraw.ini")) { + while(cmdline[0]==' ') cmdline++; + bool InQuote=false; + int count=-1; + + while(true) { + count++; + if(cmdline[count]==0) break;; + if(cmdline[count]==' '&&!InQuote) break; + if(cmdline[count]=='"') { + InQuote=!InQuote; + if(!InQuote) break; + } + } + if(cmdline[count]!=0) { + count++; + while(cmdline[count]==' ') count++; + cmdline=&cmdline[count]; + cmdlineexists=true; + } + } + + if(cmdlineexists&&strlen(cmdline)) { + strcpy_s(ini, ".\\"); + strcat_s(ini, cmdline); + HANDLE h = CreateFileA(cmdline, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + if(h!=INVALID_HANDLE_VALUE) CloseHandle(h); + else { + MessageBox(0, "You gave a command line argument to fallout, but it couldn't be matched to a file\n" \ + "Using default ddraw.ini instead", "Warning", MB_TASKMODAL); + strcpy_s(ini, ".\\ddraw.ini"); + } + } else strcpy_s(ini, ".\\ddraw.ini"); + + GetPrivateProfileStringA("Main", "TranslationsINI", "./Translations.ini", translationIni, 65, ini); + + DllMain2(); + } + return true; +} diff --git a/sfall/main.h b/sfall/main.h new file mode 100644 index 00000000..987628ae --- /dev/null +++ b/sfall/main.h @@ -0,0 +1,56 @@ +/* + * sfall + * Copyright (C) 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +//#define WIN32_LEAN_AND_MEAN +#include +#include "SafeWrite.h" +#include "Logging.h" + +extern char ini[65]; +extern char translationIni[65]; + +extern char dmModelName[65]; +extern char dfModelName[65]; + +extern bool npcautolevel; + +template T SimplePatch(DWORD addr, const char* iniSection, const char* iniKey, T defaultValue, T minValue = 0, T maxValue = INT_MAX) +{ + return SimplePatch(&addr, 1, iniSection, iniKey, defaultValue, minValue, maxValue); +} + +template T SimplePatch(DWORD *addrs, int numAddrs, const char* iniSection, const char* iniKey, T defaultValue, T minValue = 0, T maxValue = INT_MAX) +{ + T value; + char msg[255]; + value = (T)GetPrivateProfileIntA(iniSection, iniKey, defaultValue, ini); + if (value != defaultValue) { + if (value < minValue) + value = minValue; + else if (value > maxValue) + value = maxValue; + sprintf(msg, "Applying patch: %s = %d.", iniKey, value); + dlog((const char *)msg, DL_INIT); + for (int i=0; i(addrs[i], (T)value); + dlogr(" Done", DL_INIT); + } + return value; +} \ No newline at end of file diff --git a/sfall/movies.cpp b/sfall/movies.cpp new file mode 100644 index 00000000..9367ab40 --- /dev/null +++ b/sfall/movies.cpp @@ -0,0 +1,559 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include "Graphics.h" +#include +#include +#include +#include "movies.h" +#include "Logging.h" +#include "Graphics.h" +#include "Vector9x.cpp" + +static DWORD MoviePtrs[MaxMovies]; +char MoviePaths[MaxMovies*65]; + +extern IDirect3D9* d3d9; +extern IDirect3DDevice9* d3d9Device; + +#define SAFERELEASE(a) { if(a) { a->Release(); a=0; } } + +class CAllocator : public IVMRSurfaceAllocator9, IVMRImagePresenter9 { +private: + IDirect3DSurface9* surface; + IVMRSurfaceAllocatorNotify9 *pAllocNotify; + ULONG RefCount; + IDirect3DTexture9* ptex; +public: + IDirect3DTexture9* tex; + CAllocator() { + RefCount=1; + surface=NULL; + pAllocNotify=NULL; + tex=NULL; + ptex=NULL; + } + ULONG _stdcall AddRef() { + return ++RefCount; + } + ULONG _stdcall Release() { + if(--RefCount==0) { + TerminateDevice(0); + if(pAllocNotify) { + pAllocNotify->Release(); + pAllocNotify=NULL; + } + } + return RefCount; + } + HRESULT _stdcall QueryInterface(const IID &riid, void** ppvObject) { + HRESULT hr = E_NOINTERFACE; + + if( ppvObject == NULL ) { + hr = E_POINTER; + } else if( riid == IID_IVMRSurfaceAllocator9 ) { + *ppvObject = static_cast( this ); + AddRef(); + hr = S_OK; + } else if( riid == IID_IVMRImagePresenter9 ) { + *ppvObject = static_cast( this ); + AddRef(); + hr = S_OK; + } else if( riid == IID_IUnknown ) { + *ppvObject = static_cast( static_cast( this ) ); + AddRef(); + hr = S_OK; + } + + return hr; + } + + HRESULT _stdcall InitializeDevice(DWORD_PTR dwUserID, VMR9AllocationInfo *lpAllocInfo, DWORD *lpNumBuffers) { + HRESULT hr; + //Set the device + hr = pAllocNotify->SetD3DDevice(d3d9Device, d3d9->GetAdapterMonitor(0)); + //if(hr!=S_OK) return hr; + + lpAllocInfo->dwFlags |= VMR9AllocFlag_TextureSurface; + lpAllocInfo->Pool = D3DPOOL_SYSTEMMEM; + // Ask the VMR-9 to allocate the surfaces for us. + hr = pAllocNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, &surface); + if(FAILED(hr)) return hr; + + hr=surface->GetContainer(IID_IDirect3DTexture9,(void**)&ptex); + if(FAILED(hr)) { + TerminateDevice(0); + return hr; + } + + d3d9Device->CreateTexture(lpAllocInfo->dwWidth,lpAllocInfo->dwHeight,1,0,lpAllocInfo->Format, D3DPOOL_DEFAULT,&tex,NULL); + + return S_OK; + } + + HRESULT _stdcall TerminateDevice(DWORD_PTR dwID) { + SAFERELEASE(ptex); + SAFERELEASE(surface); + SAFERELEASE(tex); + return S_OK; + } + + HRESULT _stdcall GetSurface(DWORD_PTR dwUserID, DWORD SurfaceIndex, DWORD SurfaceFlags, IDirect3DSurface9 **lplpSurface) { + if(SurfaceIndex!=0) return E_FAIL; + *lplpSurface=surface; + surface->AddRef(); + return S_OK; + } + + HRESULT _stdcall AdviseNotify(IVMRSurfaceAllocatorNotify9 *lpIVMRSurfAllocNotify) { + if(lpIVMRSurfAllocNotify) { + pAllocNotify=lpIVMRSurfAllocNotify; + pAllocNotify->AddRef(); + return S_OK; + } else { + return E_FAIL; + } + } + + HRESULT _stdcall StartPresenting(DWORD_PTR dwUserID) { + return S_OK; + } + HRESULT _stdcall StopPresenting(DWORD_PTR dwUserID) { + return S_OK; + } + HRESULT _stdcall PresentImage(DWORD_PTR dwUserID,VMR9PresentationInfo *lpPresInfo) { + d3d9Device->UpdateTexture(ptex,tex); + return S_OK; + } +}; + +struct sDSTexture { + IGraphBuilder *pGraph; + ICaptureGraphBuilder2 *pBuild; + IBaseFilter *pVmr; + IVMRFilterConfig9 *pConfig; + IVMRSurfaceAllocatorNotify9 *pAlloc; + IMediaControl *pControl; + CAllocator *pMyAlloc; + IMediaSeeking *pSeek; +}; +struct sDSSound { + DWORD id; + IGraphBuilder *pGraph; + IMediaControl *pControl; + IMediaSeeking *pSeek; + IMediaEventEx *pEvent; +}; + +static IDirect3DTexture9* tex; +static sDSTexture info; + +void ResumeMovie(sDSTexture* movie) { + movie->pControl->Run(); +} +void PauseMovie(sDSTexture* movie) { + movie->pControl->Pause(); +} +void StopMovie(sDSTexture* movie) { + movie->pControl->Stop(); +} +void RewindMovie(sDSTexture* movie) { + LONGLONG time=0; + movie->pSeek->SetPositions(&time,AM_SEEKING_AbsolutePositioning,NULL,AM_SEEKING_NoPositioning); +} +void SeekMovie(sDSTexture* movie,DWORD shortTime) { + LONGLONG time=shortTime*10000; + movie->pSeek->SetPositions(&time,AM_SEEKING_AbsolutePositioning,NULL,AM_SEEKING_NoPositioning); +} +DWORD FreeMovie(sDSTexture* info) { + if(info->pControl) info->pControl->Release(); + if(info->pSeek) info->pSeek->Release(); + if(info->pAlloc) info->pAlloc->Release(); + if(info->pMyAlloc) info->pMyAlloc->Release(); + if(info->pConfig) info->pConfig->Release(); + if(info->pVmr) info->pVmr->Release(); + if(info->pGraph) info->pGraph->Release(); + if(info->pBuild) info->pBuild->Release(); + return 0; +} +DWORD CreateDSGraph(wchar_t* path,IDirect3DTexture9** tex,sDSTexture* result) { + memset(result, 0, sizeof(sDSTexture)); + + // Create the Capture Graph Builder. + HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, 0, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&result->pBuild ); + if(hr!=S_OK) return FreeMovie(result); + + // Create the Filter Graph Manager. + hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&result->pGraph); + if(hr!=S_OK) return FreeMovie(result); + + // Initialize the Capture Graph Builder. + hr = result->pBuild->SetFiltergraph(result->pGraph); + if(hr!=S_OK) return FreeMovie(result); + + hr = CoCreateInstance(CLSID_VideoMixingRenderer9, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&result->pVmr); + if(hr!=S_OK) return FreeMovie(result); + + hr = result->pGraph->AddFilter(result->pVmr, L"VMR9"); + if(hr!=S_OK) return FreeMovie(result); + + result->pVmr->QueryInterface(IID_IVMRFilterConfig9, (void**)&result->pConfig); + result->pConfig->SetRenderingMode(VMR9Mode_Renderless); + result->pVmr->QueryInterface(IID_IVMRSurfaceAllocatorNotify9,(void**)&result->pAlloc); + + result->pMyAlloc=new CAllocator(); + + result->pMyAlloc->AdviseNotify(result->pAlloc); + result->pAlloc->AdviseSurfaceAllocator(0, (IVMRSurfaceAllocator9*)result->pMyAlloc); + + hr = result->pGraph->QueryInterface(IID_IMediaControl, (void**)&result->pControl); + hr |= result->pGraph->QueryInterface(IID_IMediaSeeking, (void**)&result->pSeek); + if(hr!=S_OK) return FreeMovie(result); + + result->pGraph->RenderFile(path, NULL); + result->pControl->Run(); + + *tex=result->pMyAlloc->tex; + return 1; +} + +static DWORD PlayFrameHook3() { + __asm { + xor eax, eax; + mov edx, 0x4C9CF0; //windows message pump + call edx; + } + PlayMovieFrame(); + if(GetAsyncKeyState(VK_ESCAPE)) return 0; + _int64 pos, end; + info.pSeek->GetCurrentPosition(&pos); + info.pSeek->GetStopPosition(&end); + if(end==pos) return 0; + else return 1; +} +static void __declspec(naked) PlayFrameHook1() { + __asm { + push ecx; + call PlayFrameHook3; + pop ecx; + retn; + } +} +static void __declspec(naked) PlayFrameHook2() { + __asm { + xor eax,eax; + dec eax; + retn; + } +} + +static DWORD _stdcall PlayMovieHook2(DWORD id) { + //Get file path in unicode + wchar_t path[MAX_PATH]; + wcscpy_s(path, L"data\\art\\cuts\\"); + DWORD len=wcslen(path); + char* movie=&MoviePaths[id*65] - len; + while(movie[len]) { path[len]=movie[len]; len++; } + wcscpy_s(&path[len-3], 5, L"avi"); + + //Check for existance of file + HANDLE h=CreateFileW(path,GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + if(h==INVALID_HANDLE_VALUE) return 0; + CloseHandle(h); + + //Create texture and dshow info + if(!CreateDSGraph(path, &tex, &info)) return 0; + + GetAsyncKeyState(VK_ESCAPE); + SetMovieTexture(tex); + HookCall(0x44E937, PlayFrameHook1); + HookCall(0x44E949, PlayFrameHook2); + + return 1; +} + +static void _stdcall PlayMovieHook3() { + SafeWrite32(0x44E938, 0x3934c); + SafeWrite32(0x44E94A, 0x7a22a); + FreeMovie(&info); +} + +static const DWORD PlayMovieAddr=0x44E690; +static void __declspec(naked) PlayMovieHook() { + __asm { + cmp eax, MaxMovies; + jge fail; + push ecx; + push ebx; + push edx; + push eax; + push eax; + call PlayMovieHook2; + test eax,eax; + jnz playavi; + pop eax; + pop edx; + pop ebx; + pop ecx; + call PlayMovieAddr; + retn; +playavi: + pop eax; + pop edx; + call PlayMovieAddr; + call PlayMovieHook3; + pop ebx; + pop ecx; + xor eax, eax; + retn; +fail: + jmp PlayMovieAddr; + } +} + +static vector playingSounds; +static vector loopingSounds; +DWORD playID=0; +DWORD loopID=0; +static HWND soundwindow=0; + +static void FreeSound(sDSSound* sound) { + sound->pEvent->SetNotifyWindow(0, WM_APP, 0); + SAFERELEASE(sound->pEvent); + SAFERELEASE(sound->pSeek); + SAFERELEASE(sound->pControl); + SAFERELEASE(sound->pGraph); + delete sound; +} +void WipeSounds() { + for(DWORD d=0;did==id) { + dssound=loopingSounds[i]; + break; + } + } + } else { + for(DWORD i=0;iid==id) { + dssound=playingSounds[i]; + break; + } + } + } + if(!dssound) return 0; + LONG e=0; + LONG_PTR p1=0, p2=0; + while(!FAILED(dssound->pEvent->GetEvent(&e, &p1, &p2, 0))) { + dssound->pEvent->FreeEventParams(e, p1, p2); + if(e==EC_COMPLETE) { + if(id&0x80000000) { + LONGLONG pos=0; + dssound->pSeek->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning); + dssound->pControl->Run(); + } else { + for(DWORD i=0;iid=id; + + CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC, IID_IGraphBuilder, (void**)&result->pGraph); + result->pGraph->QueryInterface(IID_IMediaControl, (void**)&result->pControl); + if(loop) result->pGraph->QueryInterface(IID_IMediaSeeking, (void**)&result->pSeek); + else result->pSeek=0; + result->pGraph->QueryInterface(IID_IMediaEventEx, (void**)&result->pEvent); + result->pEvent->SetNotifyWindow((OAHWND)soundwindow, WM_APP, id); + result->pControl->RenderFile(path); + result->pControl->Run(); + + if(loop) loopingSounds.push_back(result); + else playingSounds.push_back(result); + + return result; +} +static const wchar_t *SoundExtensions[] = { L"mp3", L"wma", L"wav" }; +static void _stdcall SoundLoadHook2(const void*, const char* path) { + if(!path||strlen(path)<4) return; + wchar_t buf[256]; + mbstowcs_s(0, buf, path, 256); + + //CleanupSounds(); + + bool found=false; + for(int i=0;i<3;i++) { + buf[wcslen(buf)-3]=0; + wcscat_s(buf, SoundExtensions[i]); + + HANDLE h=CreateFileW(buf,GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if(h==INVALID_HANDLE_VALUE) continue; + CloseHandle(h); + found=true; + break; + } + if(!found) return; + + LoadSound(buf, true); +} +void* _stdcall PlaySfallSound(const char* path, int loop) { + wchar_t buf[256]; + mbstowcs_s(0, buf, path, 256); + if(loop) return LoadSound(buf, true); + LoadSound(buf, false); + return 0; +} +void _stdcall StopSfallSound(void* _ptr) { + sDSSound* ptr=(sDSSound*)_ptr; + for(DWORD i=0;i(0x4A36EC, "Misc", "MovieTimer_artimer4", 360, 0); + tmp = SimplePatch(0x4A3747, "Misc", "MovieTimer_artimer3", 270, 0, tmp); + tmp = SimplePatch(0x4A376A, "Misc", "MovieTimer_artimer2", 180, 0, tmp); + Artimer1DaysCheckTimer = GetPrivateProfileIntA("Misc", "MovieTimer_artimer1", 90, ini); + if (Artimer1DaysCheckTimer != 90) { + Artimer1DaysCheckTimer = max(0, min(tmp, Artimer1DaysCheckTimer)); + char s[255]; + sprintf_s(s, "Applying patch: MovieTimer_artimer1 = %d. ", Artimer1DaysCheckTimer); + dlog(s, DL_INIT); + MakeCall(0x4A378B, &Artimer1DaysCheckHook, true); + dlogr("Done", DL_INIT); + } +} \ No newline at end of file diff --git a/sfall/movies.h b/sfall/movies.h new file mode 100644 index 00000000..f147b930 --- /dev/null +++ b/sfall/movies.h @@ -0,0 +1,28 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +static const int MaxMovies=32; +extern char MoviePaths[MaxMovies*65]; + +void MoviesInit(); + +void* _stdcall PlaySfallSound(const char* path, int loop); +void _stdcall StopSfallSound(void* ptr); +void WipeSounds(); \ No newline at end of file diff --git a/sfall/queue9x.cpp b/sfall/queue9x.cpp new file mode 100644 index 00000000..f7e05026 --- /dev/null +++ b/sfall/queue9x.cpp @@ -0,0 +1,59 @@ +//#include "main.h" + +template class queue { +private: + int csize; + int asize; + int start; + T* data; +public: + queue(); + void push(T obj); + void pop(); + void clear(); + T front() const; + bool empty() const; +}; + +template +queue::queue() { + csize=0; + asize=1; + start=0; + data=new T[1]; +} + +template +void queue::push(T obj) { + if(csize==asize) { + asize*=2; + T* newData=new T[asize]; + int pos=start-1; + for(int i=0;i +void queue::pop() { + if(csize==0) return; + start++; + csize--; + if(start==asize) start=0; +} + +template +T queue::front() const { return data[start]; } + +template +void queue::clear() { csize=0; start=0; } + +template +bool queue::empty() const { return csize==0; } diff --git a/sfall/resource.h b/sfall/resource.h new file mode 100644 index 00000000..15d67d02 --- /dev/null +++ b/sfall/resource.h @@ -0,0 +1 @@ +#include "version.h" diff --git a/sfall/skills.cpp b/sfall/skills.cpp new file mode 100644 index 00000000..fb8da754 --- /dev/null +++ b/sfall/skills.cpp @@ -0,0 +1,300 @@ +/* + * sfall + * Copyright (C) 2011 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include +#include "Knockback.h" +#include "vector9x.cpp" + +struct SkillInfo { + DWORD a, b, c; //Not interested in these at the moment + DWORD image; + DWORD base; + DWORD statMulti; + int statA; + int statB; + DWORD skillPointMulti; + DWORD e, f; //nor these +}; + +struct ChanceModifier { + DWORD id; + int maximum; + int mod; +}; + +static vector SkillMaxMods; +static ChanceModifier BaseSkillMax; +static BYTE skillCosts[512*18]; +static DWORD basedOnPoints; + +static int _stdcall SkillMaxHook2(int base, DWORD critter) { + for(DWORD i=0;iskillPointMulti; + result+=info->base; + return (int)result; +} +//On input, ebx contains the skill id, ecx contains the critter, edx contains the skill id, edi contains a SkillInfo*, ebp contains the number of skill points +//On exit ebx, ecx, edi, ebp are preserved, esi contains skill base + stat bonus + skillpoints*multiplier +static void __declspec(naked) GetStatBonusHook() { + __asm { + push edx; + push ecx; + push ecx; + push ebp; + push ebx; + push edi; + call GetStatBonusHook2; + mov esi, eax; + pop ecx; + pop edx; + jmp StatBonusHookRet; + } +} + +static const DWORD SkillIncCostRet=0x4AA7C1; +static const DWORD _skill_points=0x4AA680; +static void __declspec(naked) SkillIncCostHook() { + __asm { + //eax - current skill level, ebx - current skill, ecx - num free skill points + mov edx, basedOnPoints; + test edx, edx; + jz next; + mov edx, ebx; + mov eax, esi; + call _skill_points; +next: + mov edx, ebx; + shl edx, 9; + add edx, eax; + movzx eax, skillCosts[edx]; + //eax - cost of the skill + jmp SkillIncCostRet; + } +} + +static const DWORD SkillDecCostRet=0x4AA98D; +static void __declspec(naked) SkillDecCostHook() { + __asm { + //eax - current skill level, ebx - current skill, ecx - num free skill points + mov edx, basedOnPoints; + test edx, edx; + jz next; + mov edx, ebx; + mov eax, edi; + call _skill_points; +next: + lea ecx, [eax-1]; + mov edx, ebx; + shl edx, 9; + add edx, ecx; + movzx eax, skillCosts[edx]; + //eax - cost of the skill + jmp SkillDecCostRet; + } +} + +static void __declspec(naked) SkillLevelCostHook() { + __asm { + push edx; + mov edx, ebx; + shl edx, 9; + add edx, eax; + movzx eax, skillCosts[edx]; + pop edx; + retn; + } +} + +void SkillsInit() { + MakeCall(0x4AA63C, SkillHookA, true); + MakeCall(0x004AA847, SkillHookB, true); + MakeCall(0x4AA725, SkillHookC, true); + + char buf[512], key[16], file[64]; + if(GetPrivateProfileStringA("Misc", "SkillsFile", "", buf, 62, ini)>0) { + SkillInfo *skills=(SkillInfo*)0x51D118; + + sprintf(file, ".\\%s", buf); + multipliers=new double[7*18]; + memset(multipliers, 0, 7*18*sizeof(double)); + + for(int i=0;i<18;i++) { + sprintf(key, "Skill%d", i); + if(GetPrivateProfileStringA("Skills", key, "", buf, 64, file)) { + char* tok=strtok(buf, "|"); + while(tok) { + if(strlen(tok)>=2) { + double m=atof(&tok[1]); + switch(tok[0]) { + case 's': multipliers[i*7+0]=m; break; + case 'p': multipliers[i*7+1]=m; break; + case 'e': multipliers[i*7+2]=m; break; + case 'c': multipliers[i*7+3]=m; break; + case 'i': multipliers[i*7+4]=m; break; + case 'a': multipliers[i*7+5]=m; break; + case 'l': multipliers[i*7+6]=m; break; + default: continue; + } + } + tok=strtok(0, "|"); + } + } else { + multipliers[i*7+skills[i].statA]=skills[i].statMulti; + if(skills[i].statB>=0) multipliers[i*7+skills[i].statB]=skills[i].statMulti; + } + sprintf(key, "SkillCost%d", i); + if(GetPrivateProfileStringA("Skills", key, "", buf, 512, file)) { + char* tok=strtok(buf, "|"); + DWORD upto=0; + BYTE price=1; + while(tok && upto<512) { + if(strlen(tok)) { + DWORD next=atoi(tok); + while(upto. + */ + +#pragma once + +void _stdcall SetSkillMax(DWORD critter, DWORD maximum); +void SkillsInit(); +void Skills_OnGameLoad(); \ No newline at end of file diff --git a/sfall/sound.cpp b/sfall/sound.cpp new file mode 100644 index 00000000..1cb5fa7b --- /dev/null +++ b/sfall/sound.cpp @@ -0,0 +1,90 @@ +#include "main.h" + +//#include + +static char attackerSnd[8]; +static char targetSnd[8]; +static const DWORD strncpy_ptr=0x4F014F; +static const DWORD text_object_create=0x4B036C; +static const DWORD gsound_play_sfx_file=0x4519A8; + +static void __declspec(naked) MsgCopy() { + __asm { + mov edi, [esp+0xc]; + pushad; + cmp eax, 0x56D518; + jne attacker; + lea eax, targetSnd; + jmp end; +attacker: + lea eax, attackerSnd; +end: + mov edx, edi; + mov ebx, 8; + call strncpy_ptr; + popad; + jmp strncpy_ptr; + } +} +static void __declspec(naked) DisplayMsg() { + __asm { + pushad; + cmp edx, 0x56D518; + jne attacker; + lea eax, targetSnd; + jmp end; +attacker: + lea eax, attackerSnd; +end: + mov ebx, [eax]; + test bl, bl; + jz skip; + call gsound_play_sfx_file; +skip: + popad; + jmp text_object_create; + } +} + +void PlaySfx(const char* name) { + __asm { + mov eax, name; + call gsound_play_sfx_file; + } +} + +/*struct ChangedReg { + +}; +std::vector* regChanges=0; + +void SoundExit() { + if(regChanges) { + + } +} + +static void SetupSoundAcceleration(int level) { + //RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\ControlSet001\\Hardware Profiles\\Current\\System\\CurrentControlSet\\", 0, 0, 0); +} +*/ +void SoundInit() { + int tmp; + if(tmp=GetPrivateProfileIntA("Sound", "NumSoundBuffers", 0, ini)) { + SafeWrite8(0x451129, (BYTE)tmp); + } + + if(GetPrivateProfileIntA("Sound", "AllowSoundForFloats", 0, ini)) { + HookCall(0x42B7C7, MsgCopy); + HookCall(0x42B849, DisplayMsg); + } + + //Yes, I did leave this in on purpose. Will be of use to anyone trying to add in the sound effects + if(GetPrivateProfileIntA("Sound", "Test_ForceFloats", 0, ini)) { + SafeWrite8(0x42B772, 0xeb); + } + + /*if(tmp=GetPrivateProfileIntA("Sound", "ForceSoundAcceleration", 0, ini)) { + if(tmp>=1&&tmp<=4) SetupSoundAcceleration(tmp); + }*/ +} \ No newline at end of file diff --git a/sfall/sound.h b/sfall/sound.h new file mode 100644 index 00000000..1f6354aa --- /dev/null +++ b/sfall/sound.h @@ -0,0 +1,3 @@ +void SoundInit(); +void SoundExit(); +void PlaySfx(const char* name); \ No newline at end of file diff --git a/sfall/timer.cpp b/sfall/timer.cpp new file mode 100644 index 00000000..921f2d58 --- /dev/null +++ b/sfall/timer.cpp @@ -0,0 +1,116 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +#include +#include "input.h" + +static bool Enabled; +static bool Toggled; + +static double Multi; +static DWORD StoredTickCount; +static DWORD LastTickCount; +static double TickCountFraction; + +static __int64 StartTime; + +static double Multipliers[10]; +static int Keys[10]; +static int ModKey; +static int ToggleKey; + +DWORD _stdcall FakeGetTickCount() { + //Keyboard control + if(!ModKey||(ModKey>0&&KeyDown(ModKey)) + ||(ModKey==-1&&(KeyDown(DIK_LCONTROL)||KeyDown(DIK_RCONTROL))) + ||(ModKey==-2&&(KeyDown(DIK_LMENU)||KeyDown(DIK_RMENU))) + ||(ModKey==-3&&(KeyDown(DIK_LSHIFT)||KeyDown(DIK_RSHIFT))) + ) { + + for(int i=0;i<10;i++) if(Keys[i]&&KeyDown(Keys[i])) + Multi=Multipliers[i]; + + if(ToggleKey&&KeyDown(ToggleKey)) { + if(!Toggled) { + Toggled=true; + Enabled=!Enabled; + } + } else Toggled=false; + } + + DWORD NewTickCount=GetTickCount(); + //Just in case someone's been running their computer for 49 days straight + if(NewTickCount1) { + TickCountFraction-=1; + StoredTickCount++; + } + return StoredTickCount; +} + +void _stdcall FakeGetLocalTime(LPSYSTEMTIME time) { + __int64 CurrentTime=StartTime + StoredTickCount*10000; + FileTimeToSystemTime((FILETIME*)&CurrentTime, time); +} + +//Could divide 'uDelay' by 'Multi', but doesn't seem to have any effect. +/*MMRESULT _stdcall fTimeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ) { + return timeSetEvent(uDelay, uResolution, lpTimeProc, dwUser, fuEvent); +} + +MMRESULT _stdcall fTimeKillEvent(UINT id) { + return timeKillEvent(id); +}*/ + +void TimerInit() { + StoredTickCount=0; + LastTickCount=GetTickCount(); + TickCountFraction=0; + Multi=(double)GetPrivateProfileInt("Speed", "SpeedMultiInitial", 100, ini)/100.0; + Enabled=true; + Toggled=false; + + SYSTEMTIME time; + GetLocalTime(&time); + SystemTimeToFileTime(&time, (FILETIME*)&StartTime); + + ModKey=GetPrivateProfileInt("Input", "SpeedModKey", -1, ini); + ToggleKey=GetPrivateProfileInt("Input", "SpeedToggleKey", 0, ini); + char c[2]; + char key[12]; + for(int i=0;i<10;i++) { + _itoa_s(i, c, 10); + strcpy_s(key, "SpeedKey"); + strcat_s(key, c); + Keys[i]=GetPrivateProfileInt("Input", key, 0, ini); + strcpy_s(key, "SpeedMulti"); + strcat_s(key, c); + Multipliers[i]=GetPrivateProfileInt("Speed", key, 0x00, ini)/100.0; + } +} \ No newline at end of file diff --git a/sfall/timer.h b/sfall/timer.h new file mode 100644 index 00000000..0d142655 --- /dev/null +++ b/sfall/timer.h @@ -0,0 +1,23 @@ +/* + * sfall + * Copyright (C) 2008, 2009 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +DWORD _stdcall FakeGetTickCount(); +void _stdcall FakeGetLocalTime(LPSYSTEMTIME); +void TimerInit(); \ No newline at end of file diff --git a/sfall/vector9x.cpp b/sfall/vector9x.cpp new file mode 100644 index 00000000..2cdcd357 --- /dev/null +++ b/sfall/vector9x.cpp @@ -0,0 +1,54 @@ +//#include "main.h" + +#pragma once + +template class vector { +private: + int csize; + int asize; + T* data; +public: + vector(); + void push_back(T obj); + void clear(); + unsigned int size() const; + T& operator[](int i) const; + void remove_at(int i); +}; + +template +vector::vector() { + csize=0; + asize=8; + data=new T[8]; +} + +template +void vector::push_back(T obj) { + if(csize==asize) { + asize*=2; + T* newData=new T[asize]; + for(int i=0;i +unsigned int vector::size() const { return csize; } + +template +void vector::clear() { csize=0; } + +template +T& vector::operator[](int i) const { + return data[i]; +} + +template +void vector::remove_at(int i) { + if(i>=csize) return; + for(int j=i;j. + */ + +#pragma once + +#define TARGETVERSION "Fallout 2 v1.02 US" + +#define VERSION_MAJOR 3 +#define VERSION_MINOR 6 +#define VERSION_BUILD 0 +#define VERSION_REV 0 +#ifdef WIN2K +#define VERSION_STRING "3.6 win2k" +#else +#define VERSION_STRING "3.6" +#endif + +#define CHECK_VAL (4) + diff --git a/sfall/version.rc b/sfall/version.rc new file mode 100644 index 00000000..8882831d --- /dev/null +++ b/sfall/version.rc @@ -0,0 +1,23 @@ +#include "version.h" + +1 VERSIONINFO + FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_REV + PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_REV +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" + BEGIN + VALUE "FileDescription", "sfall, built for " TARGETVERSION + VALUE "FileVersion", VERSION_STRING + VALUE "InternalName", "sfall" + VALUE "LegalCopyright", "Copyright (C) 2006-2015, Timeslip" + VALUE "ProductName", "sfall" + VALUE "ProductVersion", VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x0000 + END +END \ No newline at end of file