[RELWIP] Skyrim -Elys- Uncapper

Post » Mon May 21, 2012 3:08 pm

You can try rolling back? I think that works...

I made a post somewhere else in the Mods forum listing which files changed. You can open the properties for each of those files in Windows, and choose the "Previous Versions" tab to roll back.

Off the top of my head, the files changed in 1.4.21.0 were:
  • TESV.exe
  • SkyrimLauncher.exe
  • steam_api.dll
  • Data\Misc.bsa (not the full file name but it ends in Misc)
  • Data\Update.bsa
  • Data\Update.esm
  • and three files in the Data\Strings folder with "Update" in the file names
User avatar
Nana Samboy
 
Posts: 3424
Joined: Thu Sep 14, 2006 4:29 pm

Post » Mon May 21, 2012 4:38 am

You can try rolling back? I think that works...

Rolling back is only a temporary fix.

Most likely going forward, mods made with the CK will not support versions of Skyrim before 1.4.

So it's either roll back and use the uncapper, or go forward and use other mods, but not the uncapper.

This is why someone needs to pick up where Elys left off.
User avatar
Kelly Osbourne Kelly
 
Posts: 3426
Joined: Sun Nov 05, 2006 6:56 pm

Post » Mon May 21, 2012 9:33 am

I've got a lecturer that likes to code in Delphi so I'm going to try and convince him to teach me so I know what to do to update it for future versions of Skyrim.
User avatar
Kristian Perez
 
Posts: 3365
Joined: Thu Aug 23, 2007 3:03 am

Post » Mon May 21, 2012 4:17 am

You can try rolling back? I think that works...

How? I can find no place to force a rollback. I uninstalled entire game, and it autoupdates beyond 1.3. The install version on DVD is lower than that, so it needed to update, but I saw no option to pick and choose the update version I wanted, so I ended up with 1.41... whatever version regardless. I see no options anywhere to rollback.

Unless you mean to use windows restore, which ain't happening on this computer.
User avatar
Heather Dawson
 
Posts: 3348
Joined: Sun Oct 15, 2006 4:14 pm

Post » Mon May 21, 2012 1:00 pm

I've got a lecturer that likes to code in Delphi so I'm going to try and convince him to teach me so I know what to do to update it for future versions of Skyrim.
It is way more than that. They have to know assembly langauge and how to reverse engineer as well. It also helps to understand how the underlying game systems work.
User avatar
Sarah Knight
 
Posts: 3416
Joined: Mon Jun 19, 2006 5:02 am

Post » Mon May 21, 2012 4:54 pm

It is way more than that. They have to know assembly langauge and how to reverse engineer as well. It also helps to understand how the underlying game systems work.

Well that shatters all hope. I've got modules in assembly language so I know a bit of that. But I have absolutely no idea where to even start on the reverse engineering side.
User avatar
Joe Alvarado
 
Posts: 3467
Joined: Sat Nov 24, 2007 11:13 pm

Post » Mon May 21, 2012 3:20 am

Tempted to start donating money to someone in order to get them to take on this project.

Money seems to be a good motivator.

In fact, I probably will. If someone picks this up, you'll likely get some money from me.
User avatar
Rebecca Dosch
 
Posts: 3453
Joined: Thu Jan 18, 2007 6:39 pm

Post » Mon May 21, 2012 6:49 am

If I knew how to go about getting at the un-encrypted executable, so as to find the right portions to edit, I'd be willing to take a crack at it, but I'd have to port it to C++. I'd have no idea where to begin with that first part though.
User avatar
Emily Graham
 
Posts: 3447
Joined: Sat Jul 22, 2006 11:34 am

Post » Mon May 21, 2012 8:25 am

Lojack: Well, SD is in C++. Might want to look at Alexander Blade's source after he upddates, it may have the clues you need to accomplish this.
User avatar
WYatt REed
 
Posts: 3409
Joined: Mon Jun 18, 2007 3:06 pm

Post » Mon May 21, 2012 4:55 am

Once we get the CK there might be a way to accomplish what this mod did in a slightly uglier way that will still get the job done.

I'm thinking a script that monitors skills that are 99 or higher, and then determines how much over 99 they are and applies that as a bonus effect with a magic effect behind the scenes, then re-sets the player's skill level to 99.

This is how the very first uncappers for Morrowind worked, should still be doable.

MIGHT even be doable without the CK, but I don't know anything about SKSE or Script Dragon.

EDIT - Also, if it's just a matter of trying to track down memory addresses for where skills or something are stored, if someone could point me in the direction of a program to use in order to do this (like something I could run alongside Skyrim to test what memory is changing when a skill increases), I'd be happy to do some testing to try and track down these new addresses.

I'm just not really a programmer, but I'm happy to help in any way I can, as sadly without a mod like this, I can't really play the game anyways.
User avatar
Allison Sizemore
 
Posts: 3492
Joined: Wed Jul 19, 2006 6:09 am

Post » Mon May 21, 2012 9:02 am

Lojack: Well, SD is in C++. Might want to look at Alexander Blade's source after he upddates, it may have the clues you need to accomplish this.
It's not really a matter of C++ that's a problem for me - I've had plenty of experience reverse engineering and hacking up the Source Engine, so I know what I'm doing once I have a non-encrypted binary to work with. Anyway, SKSE and SD both just wait for Steam to unwrap the executable, then insert their code. What I'd need isn't really something to be discussed on the forums, so I'll investigate other venues.
User avatar
Elizabeth Lysons
 
Posts: 3474
Joined: Fri Feb 02, 2007 7:16 am

Post » Mon May 21, 2012 2:36 pm

Once we get the CK there might be a way to accomplish what this mod did in a slightly uglier way that will still get the job done.

I'm thinking a script that monitors skills that are 99 or higher, and then determines how much over 99 they are and applies that as a bonus effect with a magic effect behind the scenes, then re-sets the player's skill level to 99.

This is how the very first uncappers for Morrowind worked, should still be doable.

MIGHT even be doable without the CK, but I don't know anything about SKSE or Script Dragon.

EDIT - Also, if it's just a matter of trying to track down memory addresses for where skills or something are stored, if someone could point me in the direction of a program to use in order to do this (like something I could run alongside Skyrim to test what memory is changing when a skill increases), I'd be happy to do some testing to try and track down these new addresses.

I'm just not really a programmer, but I'm happy to help in any way I can, as sadly without a mod like this, I can't really play the game anyways.

Some of the stats and stuff could be floats in the memory, from what I have seen of the source code and how it references stuff, it seems similar to assembly code on finding the right info. A program you could use to look at what changes and stuff, could be the cheat engine tool at cheatengine.org. It takes some practice to learn but that might be what you are looking for.
User avatar
Max Van Morrison
 
Posts: 3503
Joined: Sat Jul 07, 2007 4:48 pm

Post » Mon May 21, 2012 11:27 am

Well, without knowing what needs to be looked for there's not much I can really do. Hopefully Lojack or someone else with the needed knowledge can find what they need. In the meantime I think I'll just play Dwarf Fortress or something.
User avatar
Mrs. Patton
 
Posts: 3418
Joined: Fri Jan 26, 2007 8:00 am

Post » Mon May 21, 2012 4:25 pm

I may also take a look at this in a couple of weeks, if no one has done it before then. I will be AFK until then. After the CK comes out next wekk we'll have a much better sense of what is possible. But this might be an interesting side project to get up and running.
User avatar
Lauren Denman
 
Posts: 3382
Joined: Fri Jun 16, 2006 10:29 am

Post » Mon May 21, 2012 5:01 pm

If that can help, below is the Pascal/Delphi (UNICODE) source code for the latest Uncapper version 1.5.4 I have released for Skyrim 1.3.10

I've isolated a few lines of your code that I think would be helpful to any programmers out there.

Click the spoiler for the whole thing, or scroll down for the snippets
Spoiler

library SKSE_Elys_Uncapper;{$R *.res}uses  Windows, SysUtils, IniFiles, Classes;type  PluginHandle = Longword;  SKSEInterface = packed record	skseVersion: Longword;	runtimeVersion: Longword;	editorVersion: Longword;	isEditor: Longword;	QueryInterface: procedure(id: Longword); cdecl;	GetPluginHandle: function: PluginHandle; cdecl;  end;  PluginInfo = packed record  const	kInfoVersion: Longword = 1;  var	infoVersion: Longword;	name: PAnsiChar;	version: Longword;  end;  TPerkByLevel = record	Level: Word;	Perks: Byte;  end;const  kPluginHandle_Invalid = $FFFFFFFF;  SKYRIM_VER = $030A0000;  *JC* These would have to be changed for a new version  SKSE_VER = $01040020;  PLUGIN_VER = $01050040;  oPLAYER = Pointer($01570334); *JC* This looks to me to be the address of the player's character. Was this value obtained through decompilation of Skyrim and examination of the assembly code?  AlterationID = 18;  ArcheryID = 8;  AlchemyID = 16;  ConjurationID = 19;  BlockID = 9;  LightArmorID = 12;  DestructionID = 20;  HeavyArmorID = 11;  LockpickingID = 14;  EnchantingID = 23;  OneHandedID = 6;  PickpocketID = 13;  IllusionID = 21;  SmithingID = 10;  SneakID = 15;  RestorationID = 22;  TwoHandedID = 7;  SpeechID = 17;  *JC* These numbers for the skills, are they also from the game data? Would they be different with Skyrim 1.4.21?var  Enabled: Boolean = False;  SkillEffectFormulasCap: Single;  FixSneakOver100CheckCode: packed Array [0 .. 5] of Byte = (	$9E, // sahf	$9B, // wait	$DF, $E0, // fstsw ax	$90, // nop	$73 // jae  );  SkillCaps: packed Array [6 .. 23] of Single;  SkillMults: Array [6 .. 23] of Single;  PerksPerLevel: Array of TPerkByLevel;  LastValidedSkill: Longword;  CurrentSkillCap : Single;  CurrentSkillLevel : Single;  pSkillCapPatch: Pointer;  pSkillCapPatch2: Pointer;  SLParam1, SLParam2 : Single;  GetActorLevel: function(a1, a2: Integer; This: Pointer): Word; cdecl;  CalculateExpForNextLevel: function( SkillLevel, SLParam1, SLParam2 : Single ): Single; cdecl;  sub_650A70: function(a1, a2, a3 : Longint; a4, a5: PSingle): Byte; cdecl;function Overwrite(Address: Pointer; Data: Pointer; Size: Longword): Boolean;var  OldFlag: Longword;begin  Result := False;  if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, @OldFlag) then	Move(Data^, Address^, Size)  else  begin	MessageBox(0, 'SKSE Elys Uncapper could not modify Skyrim in memory. PLEASE CLOSE THE GAME NOW TO AVOID POTENTIAL GAME INCONSISTANCIES OR CORRUPTION', 'Error', MB_ICONERROR);	Exit;  end;  Result := True;end;function SetFF15Call(Address: Pointer; Target: Pointer): Boolean;var  Jmp: packed record	Code: Word;	Target: Pointer;  end;begin  Jmp.Code := $15FF; *JC* This value might have to be updated  Jmp.Target := Target;  Result := Overwrite(Address, @Jmp, 6);end;Function ReplaceE8CallTarget(Caller: PByte; Target: Pointer): Boolean;var  Address: Longword;begin  Address := Longword(Target) - Longword(Caller) - 5;  Result := Overwrite(Pointer(Caller + 1), @Address, 4);end;function hook_650A70(a1, a2, a3 : Longint; a4, a5: PSingle): Byte; cdecl;begin  Result := sub_650A70(a1, a2, a3, a4, a5);  SLParam1 := a4^;  SLParam2 := a5^end;function IncreasePerkPool(NewValue: Byte): Pointer; stdcall; // thiscallconst  PerkOffset = $6C9; // offset to update with each new Skyrim version *JC* And $6C9 as wellvar  This: PByte;  Level: Word;  i: Integer;  b: Integer;begin  asm	push ecx // Thiscall fix   (Useless in this case but better safe than sorry)	mov This, ecx  end;  Level := GetActorLevel(0, 0, Pointer(oPLAYER^)); // Pointer to Player  b:= -1;  for i := 0 to High(PerksPerLevel) do	if Level < PerksPerLevel[i].Level then	  break	else if Level = PerksPerLevel[i].Level then	begin	  b := PerksPerLevel[i].Perks;	  break;	end	else	  b := PerksPerLevel[i].Perks;  if b >= 0 then	b := (This + PerkOffset)^ + b  else	b := NewValue;  if (This + PerkOffset)^ < b then	(This + PerkOffset)^ := b;  Result := This;  asm	pop ecx // Thiscall fix  (Useless in this case but better safe than sorry)  end;end;function IncreaseSkillExp(f: Single): Pointer; stdcall; // thiscallvar  This: PByte;begin  asm	push ecx // Thiscall fix   (Useless in this case but better safe than sorry)	mov This, ecx  end;  if (PSingle(This + 8)^ = 0) and (CurrentSkillLevel < SkillCaps[LastValidedSkill]) then	begin	  PSingle(This + 4)^ := 0;	  PSingle(This + 8)^ := PSingle(This + 4)^ + CalculateExpForNextLevel(CurrentSkillLevel+1, SLParam1, SLParam2);	end;  PSingle(This + 4)^ := PSingle(This + 4)^ + f * SkillMults[LastValidedSkill];  Result := This;  asm	pop ecx // Thiscall fix  (Useless in this case but better safe than sorry)  end;end;function GetSkill(i: Longword): Longword; cdecl;begin  Result := i + 6;  LastValidedSkill := Result;end;function IsValidSkill(Skill: Longword): Longbool; cdecl;begin  Result := (Skill >= 6) and (Skill < 24);  LastValidedSkill := Skillend;procedure SkillCapPatch;begin	CurrentSkillCap := SkillCaps[LastValidedSkill];  asm	fld CurrentSkillCap  end;end;procedure SkillCapPatch2;asm  mov eax, [ebp-4]  mov CurrentSkillLevel, eax  jmp SkillCapPatchend;function InitOptions: Boolean;const  INI_ERROR = 'Error: Skyrim_Elys_Uncapper.ini';  IS_INVALID_VALUE = 'http://forums.bethsoft.com/topic/1287595-relwip-skyrim-elys-uncapper/is not a valid value for';  MAX_LEVEL = 10000;var  Filename: String;  IniFile: TMemIniFile;  Strings: TStringList;  i, j: Integer;  Level: Integer;  Perks: Integer;  PerksPerLevelIndex: Integer;  function SafeReadInteger(const name: String; Default: Integer): Integer;  var	idx: Integer;  begin	idx := Strings.IndexOfName(Name);	if idx < 0 then	  Result := Default	else if TryStrToInt(Strings.ValueFromIndex[idx], Result) and (Result >= 0) and (Result <= MAX_LEVEL) then	  Exit;	Result := Default;	MessageBox(0, PChar(Strings.ValueFromIndex[idx] + IS_INVALID_VALUE + Name + '.'), INI_ERROR, MB_ICONERROR);  end;  function SafeReadFloat(const name: String; Default: Single): Single;  var	idx: Integer;  begin	idx := Strings.IndexOfName(Name);	if idx < 0 then	  Result := Default	else if TryStrToFloat(Strings.ValueFromIndex[idx], Result) and (Result >= 0) and (Result <= MAX_LEVEL) then	  Exit;	Result := Default;	MessageBox(0, PChar(Strings.ValueFromIndex[idx] + IS_INVALID_VALUE + Name + '.'), INI_ERROR, MB_ICONERROR);  end;  function SafeReadBool(const name: String; Default: Boolean): Boolean;  var	idx: Integer;  begin	idx := Strings.IndexOfName(Name);	if idx < 0 then	  Result := Default	else if not TryStrToBool(Strings.ValueFromIndex[idx], Result) then	begin	  Result := Default;	  MessageBox(0, PChar(Strings.ValueFromIndex[idx] + IS_INVALID_VALUE + Name + '.'), INI_ERROR, MB_ICONERROR);	  Exit;	end;  end;begin  Result := True;  SetLength(Filename, MAX_PATH + 1);  GetModuleFileNameW(HInstance, @Filename[1], MAX_PATH + 1);  IniFile := TMemIniFile.Create(ExtractFilePath(Filename) + 'SKSE_Elys_Uncapper.ini');  PerksPerLevelIndex := 0;  try	Strings := TStringList.Create;	try	  FormatSettings.DecimalSeparator := '.';	  SetLength(TrueBoolStrs, 2);	  TrueBoolStrs[0] := 'True';	  TrueBoolStrs[1] := '1';	  SetLength(FalseBoolStrs, 2);	  FalseBoolStrs[0] := 'False';	  FalseBoolStrs[1] := '0';	  IniFile.ReadSectionValues('General', Strings);	  Enabled := SafeReadBool('bEnabled', False);	  SkillEffectFormulasCap := SafeReadInteger('iSkillEffectFormulasCap', 100);	  Strings.Clear;	  IniFile.ReadSectionValues('SkillCaps', Strings);	  SkillCaps[AlterationID] := SafeReadInteger('iAlteration', 100);	  SkillCaps[ArcheryID] := SafeReadInteger('iArchery', 100);	  SkillCaps[AlchemyID] := SafeReadInteger('iAlchemy', 100);	  SkillCaps[ConjurationID] := SafeReadInteger('iConjuration', 100);	  SkillCaps[BlockID] := SafeReadInteger('iBlock', 100);	  SkillCaps[LightArmorID] := SafeReadInteger('iLightArmor', 100);	  SkillCaps[DestructionID] := SafeReadInteger('iDestruction', 100);	  SkillCaps[HeavyArmorID] := SafeReadInteger('iHeavyArmor', 100);	  SkillCaps[LockpickingID] := SafeReadInteger('iLockpicking', 100);	  SkillCaps[EnchantingID] := SafeReadInteger('iEnchanting', 100);	  SkillCaps[OneHandedID] := SafeReadInteger('iOneHanded', 100);	  SkillCaps[PickpocketID] := SafeReadInteger('iPickpocket', 100);	  SkillCaps[IllusionID]:= SafeReadInteger('iIllusion', 100);	  SkillCaps[SmithingID] := SafeReadInteger('iSmithing', 100);	  SkillCaps[SneakID] := SafeReadInteger('iSneak', 100);	  SkillCaps[RestorationID]:= SafeReadInteger('iRestoration', 100);	  SkillCaps[TwoHandedID]  := SafeReadInteger('iTwoHanded', 100);	  SkillCaps[SpeechID] := SafeReadInteger('iSpeech', 100);	  Strings.Clear;	  IniFile.ReadSectionValues('SkillExpGainMults', Strings);	  SkillMults[AlterationID] := SafeReadFloat('fAlteration', 1);	  SkillMults[ArcheryID] := SafeReadFloat('fArchery', 1);	  SkillMults[AlchemyID] := SafeReadFloat('fAlchemy', 1);	  SkillMults[ConjurationID] := SafeReadFloat('fConjuration', 1);	  SkillMults[BlockID] := SafeReadFloat('fBlock', 1);	  SkillMults[LightArmorID] := SafeReadFloat('fLightArmor', 1);	  SkillMults[DestructionID] := SafeReadFloat('fDestruction', 1);	  SkillMults[HeavyArmorID] := SafeReadFloat('fHeavyArmor', 1);	  SkillMults[LockpickingID] := SafeReadFloat('fLockpicking', 1);	  SkillMults[EnchantingID] := SafeReadFloat('fEnchanting', 1);	  SkillMults[OneHandedID] := SafeReadFloat('fOneHanded', 1);	  SkillMults[PickpocketID] := SafeReadFloat('fPickpocket', 1);	  SkillMults[IllusionID] := SafeReadFloat('fIllusion', 1);	  SkillMults[SmithingID] := SafeReadFloat('fSmithing', 1);	  SkillMults[SneakID] := SafeReadFloat('fSneak', 1);	  SkillMults[RestorationID] := SafeReadFloat('fRestoration', 1);	  SkillMults[TwoHandedID] := SafeReadFloat('fTwoHanded', 1);	  SkillMults[SpeechID] := SafeReadFloat('fSpeech', 1); *JC* These lines don't need to be changed since they just read the ini. Don't want users of the new version to be unable to use their old settings, now do we? ~	  Strings.Clear;	  IniFile.ReadSectionValues('PerksPerLevelAndAbove', Strings);	  SetLength(PerksPerLevel, Strings.Count);	  For i := 0 to Strings.Count - 1 do		if TryStrToInt(Strings.Names[i], Level) and (Level > 0) and (Level <= MAX_LEVEL) then		  if TryStrToInt(Strings.ValueFromIndex[i], Perks) and (Perks >= 0) and (Perks <= 255) then		  begin			PerksPerLevel[PerksPerLevelIndex].Level := Level;			PerksPerLevel[PerksPerLevelIndex].Perks := Perks;			Inc(PerksPerLevelIndex);		  end		  else			MessageBox(0, PChar(Strings[i] + ' is not a correct Perks value for [PerksPerLevelAndAbove].'), INI_ERROR, MB_ICONERROR)		else		  MessageBox(0, PChar(Strings[i] + ' is not a correct Level value for [PerksPerLevelAndAbove].'), INI_ERROR, MB_ICONERROR);	finally	  Strings.Free;	end;	SetLength(PerksPerLevel, PerksPerLevelIndex);	for j := High(PerksPerLevel) - 1 downto 1 do	  for i := 0 to j do		if PerksPerLevel[i].Level > PerksPerLevel[i + 1].Level then		begin		  Level := PerksPerLevel[i].Level;		  Perks := PerksPerLevel[i].Perks;		  PerksPerLevel[i].Level := PerksPerLevel[i + 1].Level;		  PerksPerLevel[i].Perks := PerksPerLevel[i + 1].Perks;		  PerksPerLevel[i + 1].Level := Level;		  PerksPerLevel[i + 1].Perks := Perks;		end;  except	MessageBox(0, 'SKSE Elys Uncapper encountered an error while attempting to read SKSE_Elys_Uncapper.ini. The plugin is not enabled', 'Error', MB_ICONERROR);	Result := False;  end;  IniFile.Free;end;Function SKSEPlugin_Query(const skse: SKSEInterface; var Info: PluginInfo): Boolean; cdecl;begin  Info.infoVersion := PluginInfo.kInfoVersion;  Info.name := 'SKSE_Elys_Uncapper';  Info.version := PLUGIN_VER;  Result := False;  if skse.isEditor <> 0 then	Exit;  if skse.skseVersion < SKSE_VER then  begin	MessageBox(0, 'SKSE Elys Uncapper requires SKSE Version 1.4.2 or higher.', 'Error: Plugin not enabled', MB_ICONERROR);	Exit;  end;  if skse.runtimeVersion <> SKYRIM_VER then  begin	MessageBox(0, 'SKSE Elys Uncapper only supports Skyrim Version 1.3.10.', 'Error: Plugin not enabled', MB_ICONERROR);	Exit;  end;  *JC* Suggestion: Instead of this line, let the plug in run. For each value that needs to be fetched from the game executable in memory, if the fetch succeeds, that value does not have to be changed in a new version of this Uncapper. If it fails, well, create a log where each entry is either "PerkOffset - $6C9 - FAILED" or "oPlayer - $01570334 - SUCCEEDED" I'll go into more detail later.  Result := True;end;Function SKSEPlugin_Load(const skse: SKSEInterface): Boolean; cdecl;begin  Result := False;  If not InitOptions then	Exit;  if not Enabled then  begin	Result := True;	Exit;  end;  GetActorLevel := Pointer($00A512D0); // Don't forget to update the call used with new Player pointer  CalculateExpForNextLevel := Pointer($0088DB20);  sub_650A70 := Pointer($00650A70);  if not ReplaceE8CallTarget(PByte($0088DCE3), @IsValidSkill) then // IsValidSkill	Exit;  if not ReplaceE8CallTarget(PByte($0088DA62), @GetSkill) then // GetSKill	Exit;  pSkillCapPatch := @SkillCapPatch;  pSkillCapPatch2 := @SkillCapPatch2;  if not SetFF15Call(PByte(@CalculateExpForNextLevel)+9, @pSkillCapPatch) then // Skill Exp Curve Cap	Exit;  if not SetFF15Call(Pointer($0088DE80), @pSkillCapPatch) then // Skill Cap	Exit;  if not SetFF15Call(Pointer($0088DD27), @pSkillCapPatch2) then // Skill Cap 2	Exit;  if not ReplaceE8CallTarget(PByte($0088DD6A), @hook_650A70) then // sub_650A70 hook;	Exit;  if not ReplaceE8CallTarget(PByte($009EA32D), @IncreasePerkPool) then // Perk Management. Dont forget update offset inside function	Exit;  if not ReplaceE8CallTarget(PByte($0088DE74), @IncreaseSkillExp) then // Skill Exp Mult	Exit;  if not Overwrite(Pointer($006537A8), @SkillEffectFormulasCap, 4) then // Uncap formulas	Exit;  if not Overwrite(Pointer($007D2024), @FixSneakOver100CheckCode, 6) then // Sneak Formula Cap Fix  *JC* All of the $00xxxxxx values would have to be updated.	Exit;  Result := True;end;exports  SKSEPlugin_Query, SKSEPlugin_Load;beginend.


Here are the lines that probably have to be changed in a new version:
Spoiler


  SKYRIM_VER = $030A0000;   SKSE_VER = $01040020;  PLUGIN_VER = $01050040;  oPLAYER = Pointer($01570334);PerkOffset = $6C9; // offset to update with each new Skyrim version  GetActorLevel := Pointer($00A512D0); // Don't forget to update the call used with new Player pointer  CalculateExpForNextLevel := Pointer($0088DB20);  sub_650A70 := Pointer($00650A70);  if not ReplaceE8CallTarget(PByte($0088DCE3), @IsValidSkill) then // IsValidSkill  if not ReplaceE8CallTarget(PByte($0088DA62), @GetSkill) then // GetSKillif not SetFF15Call(Pointer($0088DE80), @pSkillCapPatch) then // Skill Capif not SetFF15Call(Pointer($0088DD27), @pSkillCapPatch2) then // Skill Cap 2if not ReplaceE8CallTarget(PByte($0088DD6A), @hook_650A70) then // sub_650A70 hook;if not ReplaceE8CallTarget(PByte($009EA32D), @IncreasePerkPool) then // Perk Management. Dont forget update offset inside functionif not ReplaceE8CallTarget(PByte($0088DE74), @IncreaseSkillExp) thenif not Overwrite(Pointer($006537A8), @SkillEffectFormulasCap, 4) then // Uncap formulasif not Overwrite(Pointer($007D2024), @FixSneakOver100CheckCode, 6) then // Sneak Formula Cap Fix

I'd post more, but it's 3:15 AM right now. I do have a suggestion though, for Elys or anyone who is willing... On the off chance that some of these values might be the same (though the way Elys described it seems to point toward all of them being different) in 1.4.21, take out the check for the Skyrim version and add a way to write a log file, so each time a value is accessed, if it fails or succeeds write a line to the log file for each item. Each line would have the name, address and result. Example:
PerkOffset - $6C9 - FAILED
oPlayer - $01570334 - SUCCEEDED
User avatar
carla
 
Posts: 3345
Joined: Wed Aug 23, 2006 8:36 am

Post » Mon May 21, 2012 3:43 pm

I've isolated a few lines of your code that I think would be helpful to any programmers out there.
......
PerkOffset - $6C9 - FAILED
oPlayer - $01570334 - SUCCEEDED

I can tell you absolutely all addresses have changed, as well as the code used at any of these addresses. I've checked them all. That's when I realized I had to do a code inspection from scratch again if I wanted to update the Uncapper for Skyrim 1.4.1.

The PerkOffset value, in fact points to the "non-allocated Perks points" variable in the Actor class. And it has not changed since just after Skyrim 1.1.21. And it is the same in 1.4.27.
Also you would not really need to use oPlayer in the perk related code, you could just replace it with "This", it's the same value for the player in this Perk management case. I just happend to use oPlayer instead of This for non specific reason.


Anyway the new code that increase Perk Points at level up is found at 0087BCB9h in Skyrim 1.4.27, and is completely different. It is in fact a lot better than originally.
The first time I saw how they increased Perks (and other variables) in previous version of Skyrim, I was like "WTH.. why is there so many indirections in the code just to update a value?".
Skyrim version 1.4.21 looks a lot better (from an Assembly perspective) than previous versions. Before for example to increase the Perks, required not only few lines of assembly but also a call to a function (function modified by the Uncapper). While in 1.4.27, it just takes 2 lines of assembly:

mov eax, 0137DC6Chinc byte ptr [eax+6C9h]

0137DC6Ch being the pointer to the Player actor class instance.

Perks management wise, the Uncapper code needs only a minor modification to work with the latest version. That's however as far as I got. All the other code for the skill level cap, skill level formulas..... requires to be found again in Skyrim 1.4.21.
User avatar
Alba Casas
 
Posts: 3478
Joined: Tue Dec 12, 2006 2:31 pm

Post » Mon May 21, 2012 6:32 am

This is incomplete and untested, but I think all of the hook addresses are correct. Due to inlining, some code needed to be changed. I, uh, also wrote it in C++ and didn't port most of the logic over.

Spoiler
#include "skse/PluginAPI.h"#include "skse/skse_version.h"#include "skse/GameReferences.h"#include "skse/SafeWrite.h"IDebugLog	gLog("skill_uncapper.log");typedef UInt32 (* _GetActorLevel)(UInt32 arg0, UInt32 arg1, PlayerCharacter * player);_GetActorLevel GetActorLevel = (_GetActorLevel)0x008C30F0;typedef float (* _CalculateExpForNextLevel)(float skillLevel, float param1, float param2);_CalculateExpForNextLevel CalculateExpForNextLevel = (_CalculateExpForNextLevel)0x00751250;// was sub_650A70typedef bool (* _GetSkillCoefficients)(UInt32 arg0, float * arg1, float * arg2, float * arg3, float * arg4);_GetSkillCoefficients GetSkillCoefficients = (_GetSkillCoefficients)0x005A85F0;float	g_skillCaps[24];float	g_skillMults[24];UInt32	g_lastUsedSkill;float	g_currentSkillLevel;float	g_slParam1;float	g_slParam2;const UInt32 IsValidSkillHook_retn = 0x00751624;void __declspec(naked) IsValidSkillHook(void){	__asm	{		// save last used skill		mov		[g_lastUsedSkill], edi		// overwritten code		lea		eax, [edi-6]		mov		ebp, ecx		// return to original code		jmp		[IsValidSkillHook_retn]	}}const UInt32 GetSkillHook_retn = 0x007514F7;void __declspec(naked) GetSkillHook(void){	__asm	{		// save last used skill		mov		[g_lastUsedSkill], esi		// overwritten code		mov		ecx, 0x3F800000		// return to original code		jmp		[GetSkillHook_retn]	}}// trashes eax, but that is ok due to the use casesvoid __declspec(naked) GetSkillCap(void){	__asm	{		mov		eax, [g_lastUsedSkill]		fld		[g_skillCaps + eax * 4]		retn	}}const UInt32 GetSkillCapAndSaveCurLevel_retn = 0x00751651;void __declspec(naked) GetSkillCapAndSaveCurLevel(void){	__asm	{		// save current skill level		mov		eax, [esp + 8]		mov		[g_currentSkillLevel], eax		// replace overwritten code with return g_skillCaps[g_lastUsedSkill];		mov		eax, [g_lastUsedSkill]		fld		[g_skillCaps + eax * 4]		// return to original code		jmp		[GetSkillCapAndSaveCurLevel_retn]	}}bool GetSkillCoefficientsHook(UInt32 arg0, float * arg1, float * arg2, float * arg3, float * arg4){	bool result = GetSkillCoefficients(arg0, arg1, arg2, arg3, arg4);	g_slParam1 = *arg3;	g_slParam2 = *arg4;	return result;}void IncreasePerkPool(void){	PlayerCharacter	* player = *g_thePlayer;	player->numPerkPoints++;	// ###}void InstallHooks(void){	// IsValidSkill hook	WriteRelJump(0x0075161F, (UInt32)IsValidSkillHook);	// GetSkill hook	WriteRelJump(0x007514F2, (UInt32)GetSkillHook);	// skill exp curve cap	WriteRelCall(0x00751257, (UInt32)GetSkillCap);	SafeWrite8(0x00751257 + 5, 0x90);	// skill cap	WriteRelCall(0x0075174D, (UInt32)GetSkillCap);	SafeWrite8(0x0075174D + 5, 0x90);	// skill cap 2	WriteRelJump(0x0075164B, (UInt32)GetSkillCapAndSaveCurLevel);	// GetSkillCoefficients hook	WriteRelCall(0x0075168B, (UInt32)GetSkillCoefficientsHook);	// IncreasePerkPool hook	WriteRelCall(0x0087BCB9, (UInt32)IncreasePerkPool);	SafeWrite8(0x0087BCB9 + 5, 0x90);	// IncreaseSkillExp hook	// 0075173A	// uncap formulas	// 005AA2F7	// sneak formula cap fix	// 006C78EA}bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info){	info->infoVersion =	PluginInfo::kInfoVersion;	info->name =		"skill uncapper";	info->version =		1;	if(skse->isEditor) return false;	if(skse->runtimeVersion != RUNTIME_VERSION_1_4_21_0) return false;	return true;}bool SKSEPlugin_Load(const SKSEInterface * skse){	InstallHooks();	return true;}
User avatar
Sabrina garzotto
 
Posts: 3384
Joined: Fri Dec 29, 2006 4:58 pm

Post » Mon May 21, 2012 8:49 am

This is incomplete and untested, but I think all of the hook addresses are correct. Due to inlining, some code needed to be changed. I, uh, also wrote it in C++ and didn't port most of the logic over.

Spoiler
#include "skse/PluginAPI.h"#include "skse/skse_version.h"#include "skse/GameReferences.h"#include "skse/SafeWrite.h"IDebugLog	gLog("skill_uncapper.log");typedef UInt32 (* _GetActorLevel)(UInt32 arg0, UInt32 arg1, PlayerCharacter * player);_GetActorLevel GetActorLevel = (_GetActorLevel)0x008C30F0;typedef float (* _CalculateExpForNextLevel)(float skillLevel, float param1, float param2);_CalculateExpForNextLevel CalculateExpForNextLevel = (_CalculateExpForNextLevel)0x00751250;// was sub_650A70typedef bool (* _GetSkillCoefficients)(UInt32 arg0, float * arg1, float * arg2, float * arg3, float * arg4);_GetSkillCoefficients GetSkillCoefficients = (_GetSkillCoefficients)0x005A85F0;float	g_skillCaps[24];float	g_skillMults[24];UInt32	g_lastUsedSkill;float	g_currentSkillLevel;float	g_slParam1;float	g_slParam2;const UInt32 IsValidSkillHook_retn = 0x00751624;void __declspec(naked) IsValidSkillHook(void){	__asm	{		// save last used skill		mov		[g_lastUsedSkill], edi		// overwritten code		lea		eax, [edi-6]		mov		ebp, ecx		// return to original code		jmp		[IsValidSkillHook_retn]	}}const UInt32 GetSkillHook_retn = 0x007514F7;void __declspec(naked) GetSkillHook(void){	__asm	{		// save last used skill		mov		[g_lastUsedSkill], esi		// overwritten code		mov		ecx, 0x3F800000		// return to original code		jmp		[GetSkillHook_retn]	}}// trashes eax, but that is ok due to the use casesvoid __declspec(naked) GetSkillCap(void){	__asm	{		mov		eax, [g_lastUsedSkill]		fld		[g_skillCaps + eax * 4]		retn	}}const UInt32 GetSkillCapAndSaveCurLevel_retn = 0x00751651;void __declspec(naked) GetSkillCapAndSaveCurLevel(void){	__asm	{		// save current skill level		mov		eax, [esp + 8]		mov		[g_currentSkillLevel], eax		// replace overwritten code with return g_skillCaps[g_lastUsedSkill];		mov		eax, [g_lastUsedSkill]		fld		[g_skillCaps + eax * 4]		// return to original code		jmp		[GetSkillCapAndSaveCurLevel_retn]	}}bool GetSkillCoefficientsHook(UInt32 arg0, float * arg1, float * arg2, float * arg3, float * arg4){	bool result = GetSkillCoefficients(arg0, arg1, arg2, arg3, arg4);	g_slParam1 = *arg3;	g_slParam2 = *arg4;	return result;}void IncreasePerkPool(void){	PlayerCharacter	* player = *g_thePlayer;	player->numPerkPoints++;	// ###}void InstallHooks(void){	// IsValidSkill hook	WriteRelJump(0x0075161F, (UInt32)IsValidSkillHook);	// GetSkill hook	WriteRelJump(0x007514F2, (UInt32)GetSkillHook);	// skill exp curve cap	WriteRelCall(0x00751257, (UInt32)GetSkillCap);	SafeWrite8(0x00751257 + 5, 0x90);	// skill cap	WriteRelCall(0x0075174D, (UInt32)GetSkillCap);	SafeWrite8(0x0075174D + 5, 0x90);	// skill cap 2	WriteRelJump(0x0075164B, (UInt32)GetSkillCapAndSaveCurLevel);	// GetSkillCoefficients hook	WriteRelCall(0x0075168B, (UInt32)GetSkillCoefficientsHook);	// IncreasePerkPool hook	WriteRelCall(0x0087BCB9, (UInt32)IncreasePerkPool);	SafeWrite8(0x0087BCB9 + 5, 0x90);	// IncreaseSkillExp hook	// 0075173A	// uncap formulas	// 005AA2F7	// sneak formula cap fix	// 006C78EA}bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info){	info->infoVersion =	PluginInfo::kInfoVersion;	info->name =		"skill uncapper";	info->version =		1;	if(skse->isEditor) return false;	if(skse->runtimeVersion != RUNTIME_VERSION_1_4_21_0) return false;	return true;}bool SKSEPlugin_Load(const SKSEInterface * skse){	InstallHooks();	return true;}

Oh nice :ooo:
User avatar
Donatus Uwasomba
 
Posts: 3361
Joined: Sun May 27, 2007 7:22 pm

Post » Mon May 21, 2012 3:43 am

I have IDA Pro Free, which I got originally to see just what the game .kkrieger looked like in Assembly (It's a game with near Doom 3 level graphics, but is only 96kb in size - massive procedural generation and compression)

Using IDA, how do you go about looking for what you need, exactly? I did disassemble the Skyrim exe and there is a lot of code there. It's a big executable, and I'm only vaguely familiar with some x86 assembly. I did take an assembly language class in college but that used the MIPS instruction set, which for all intents and purposes is far simpler than the x86 instruction set.

I know in IDA there are various ways to look through the code - such as through names and strings, but the lists of these are huge - for strings there are over 100,000 entries. And scrolling through always bumped me back to the entry I had selected.

With ianpatt's post, perhaps the Uncapper can be updated after all? But I'd like to help in any way I can, since I'm a recently graduated computer science major and I want to make sure I don't get rusty in programming.
User avatar
Vickytoria Vasquez
 
Posts: 3456
Joined: Thu Aug 31, 2006 7:06 pm

Post » Mon May 21, 2012 5:53 am

I'm so glad different people are looking at this.
User avatar
Laura Richards
 
Posts: 3468
Joined: Mon Aug 28, 2006 4:42 am

Post » Mon May 21, 2012 1:50 pm

Using IDA, how do you go about looking for what you need, exactly?
Discussing such things on official boards is highly frowned upon. Bethesda gives us great latitude in what we do with their game, but we do not abuse that privilege. You'll have to take the discussion somewhere else.
User avatar
Micah Judaeah
 
Posts: 3443
Joined: Tue Oct 24, 2006 6:22 pm

Post » Mon May 21, 2012 9:20 am

Since ianpatt kindly provided the new needed addresses for the Uncapper, I was able to update it.

So you can now download V1.55. No new features, just support of Skyrim 1.4.21.

You can all thanks ianpatt for it :biggrin:

I'm gonna reply to some other posts in this thread as well as posting the new source code, but I'm in a rush, so later.....
User avatar
Spooky Angel
 
Posts: 3500
Joined: Thu Aug 10, 2006 5:41 pm

Post » Mon May 21, 2012 5:06 pm

Woot! Thank you so much Elys and ianpatt. You made my whole day.
User avatar
hannaH
 
Posts: 3513
Joined: Tue Aug 15, 2006 4:50 am

Post » Mon May 21, 2012 5:29 pm

I am very glad to have the work Elys had provided thus far, and his mod has been amazing. Let me be another of the masses to thank you for your wonderful work so far, Elys! And let's not forget to give a shout-out to ianpatt for his input. I wish I didn't have my Steam set to auto-update Skyrim, as I'd keep all the problems and bugs in the game to keep this mod. (And now I get both, which makes me very happy.) Thank you for your hard work, and sharing it with us masses.
User avatar
Karl harris
 
Posts: 3423
Joined: Thu May 17, 2007 3:17 pm

Post » Mon May 21, 2012 4:15 am

Thank you very much for the update and ianpatt once again you helped with TESVAL, Ely's uncapper and continued great work with script extenders across games, Thank you.
User avatar
carla
 
Posts: 3345
Joined: Wed Aug 23, 2006 8:36 am

PreviousNext

Return to V - Skyrim