• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Accessing memory from the script - it's time of the revolution

Status
Not open for further replies.
Level 9
Joined
Jul 30, 2012
Messages
156
Why do we have to "clean" when reinterpreting the bits of a real as an int and vice versa? In the "forgot to clean" case, why does the execution stops/"crashes", as if the variable was uninitialized?

The Jass VM, like any other real machine, has CPU registers. There are 256 registers, that can store any 32-bit value. But the difference here is that all data nodes in the VM are type-safe (By "data nodes" I'm referring to all registers and local/global variables).

This means that they have not only a value, but they also have a type (the types are code(3), integer(4), real(5), string(6), handle(7), boolean(8), and all the array variants of these(9~13). And the key point of the VM is the fact that all operations that write to variables are type-safe (the type of the data you're writing must be of the same type of the variable).

So, let's take a look at function realToIndex. Although this function returns an integer, the instruction return r is actually returning a real variable! What this instruction really does is to set the value of register R0 to the value of r, and set it's type to 5 (real).

Now look at the line set r_as_int = realToIndex(r). This line is attempting to write the return value of the function (which is in register R0) to the variable r_as_int. But the problem is that this variable is an integer (type 4), while the register R0 has been initialized with type 5 (real). Therefore, this operation will fail, nothing will be written to r_as_int, and so the next line of code will crash, because the variable has not been initialized.

The trick of using a clean function works, because there is no type-safety in function arguments. Despite the fact that function cleanInt is expecting an integer, you can PUSH R0 to that function just fine, even if R0 holds a real. The function will read it as an integer, and the instruction return i will re-initialize R0 as an integer, making the code work.
 
Level 13
Joined
Nov 7, 2014
Messages
571
@leandrotp, thanks! Really awesome insights.

Edit:

So, let's take a look at function realToIndex. Although this function returns an integer, the instruction return r is actually returning a real variable! What this instruction really does is to set the value of register R0 to the value of r, and set it's type to 5 (real).

Now look at the line set r_as_int = realToIndex(r). This line is attempting to write the return value of the function (which is in register R0) to the variable r_as_int. But the problem is that this variable is an integer (type 4), while the register R0 has been initialized with type 5 (real). Therefore, this operation will fail, nothing will be written to r_as_int, and so the next line of code will crash, because the variable has not been initialized.

How come then the H2I "family" of functions in older versions (1.23-) did not require a "clean" function.

JASS:
function H2I takes handle h returns integer
    return h
    return 0
endfunction

local integer handle_as_int = H2I(<some-handle>)
call BJDebugMsg(I2S(handle_as_int)) // no crash, no "clean" function

I.e isn't the instruction return h in the H2I function returning a handle variable, i.e setting the value of register R0 to the handle's id and the type to handle(7) which then gets written to the integer variable handle_as_int successfully even though the types handle(7) of the return value and the integer(4) of the variable don't match?
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
How are the handle types unit, item, destructable, ability, buf and upgrade (the types one can edit in the Object Editor) organized in memory?
I always though of them as some kind of C/C++ structs/classes (e.g: unit_handle.armor = 4; // unit's armor is now 4) but obviously that's not the case because in the Object Editor they have a lot of fields that are identified by "rawcodes" (integers): udef - unit armor, unam - unit name etc.

So I guess some of their fields are stored in a struct (i.e: a units' x, y and z fields) but others like a unit's [default] armor are stored in a hashtable of Integer to String|Integer|Real.

Would the Unit's definition look something like this:
JASS:
// this is invalid C++ for sure (I don't know C++)
struct Unit {
    float position[3]; // position[0] = x, position[1] = y, position[2] = z
    // other fields that are not exposed to the Object Editor

    HashMap<int, Integer_or_String_or_Real> props; // the fields/props that are exposed in the Object Editor like a unit's armor (udef)
}

The above is my interpretation which is probably way off so that's why I'm asking.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
All of the data is stored in the SLKs and custom definitions in the map.
Every instance of an object obviously only needs to store its own data (e.g. current HP is owned by the instance, maximum HP is in the SLK or custom definition).
As to the IDs - I imagine they are just struct member names, but who knows.
 
Level 1
Joined
May 1, 2013
Messages
7
Hello guys, i'm just a hover over there.

I charmed with this topic, but when i try to use the JASS codes, then i got this error list:

3o23YAV.jpg


It seem like very clear to see the bugs, i don't know, but you guys determined the code is wc3 memory hack.
That mix is over my knowledge at all!

Can anyone tell me where is my mistakes, or how a right way to use the codes pls :)

Thank you.
 
Level 1
Joined
Mar 14, 2016
Messages
1
Hi,
I am testing the Version Lib.

Shouldn`t
function ImLazy

end with a return? i.e.
function ImLazy takes nothing returns nothing
return
endfunction


I found that otherwise war3 will reject the script, if the return statement does not appear inside the function, in 1.26.
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
which is still possible in patch 1.27
Sadly this doesn't work in 1.27b. My map just crashes whenever it hits ~75% on loading.

EDIT:
I wonder what exactly did they change in 1.27b so that memory stuffs no longer work? I checked the change log but nothing seems to have anything to do with it.:\
 
Last edited:
Level 9
Joined
Jul 30, 2012
Messages
156
Sadly this doesn't work in 1.27b. My map just crashes whenever it hits ~75% on loading.

EDIT:
I wonder what exactly did they change in 1.27b so that memory stuffs no longer work? I checked the change log but nothing seems to have anything to do with it.:\

It does work, just needed some updated offsets. I have updated it to work with 1.27b and 1.28, please use the latest updated libraries from the 1st post and tell me if the problem persists.
 
  • Like
Reactions: pyf

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
It does work, just needed some updated offsets. I have updated it to work with 1.27b and 1.28, please use the latest updated libraries from the 1st post and tell me if the problem persists.
The thing is I'm not exactly using this, I use Drac's jass memory API. Well, anyway I only need the GetMouseX/Y natives, can you make it using this?
 
Level 9
Joined
Jul 30, 2012
Messages
156
The thing is I'm not exactly using this, I use Drac's jass memory API. Well, anyway I only need the GetMouseX/Y natives, can you make it using this?

It's done, GetMouseX/Y/Z functions were ported from Memory Hack and are working correctly in patches 1.27b and 1.28. Check the 1st post and get updated versions of libraries Memory, Version and Utils
 
Can I also add this?

JASS:
function GetEventBaseDamage takes nothing returns real
    local real delta = (GetUnitArmor(GetTriggerUnit()) * 0.06)/(1 + GetUnitArmor(GetTriggerUnit()))
    return GetEventDamage() / delta
endfunction

EDIT:

I made a library for basic ability cooldown and manacost retrieval. Thanks and more power to you, leandrotp

JASS:
library AbilityParser initializer Init requires /*

    */Utils, /* By leandrotp, https://www.hiveworkshop.com/threads/accessing-memory-from-the-script-its-time-of-the-revolution.279262/
    */
   
    struct Ability extends array
        private static integer numInstances = 0
           
        private thistype recCount
       
        private thistype next
        private thistype prev
       
        method deallocate takes nothing returns nothing
            debug if recCount != 0 then
                debug call BJDebugMsg("Error, cannot deallocate an instance more than once")
                debug set recCount = 1/0
            debug endif
           
            set recCount = thistype(0).recCount
            set thistype(0).recCount = this
            set numInstances = numInstances - 1
        endmethod
       
        static method allocate takes nothing returns thistype
            local thistype this = thistype(0).recCount
            if this.recCount == 0 then
                set this = this + 1
                set numInstances = numInstances + 1
                set thistype(0).recCount = this
                return this
            endif
            set thistype(0).recCount = this.recCount
            set this.recCount = 0
            set numInstances = numInstances + 1
            return this
        endmethod
       
        method pop takes nothing returns nothing
            set next.prev = prev
            set prev.next = next
        endmethod
       
        method push takes nothing returns nothing
            set next = 0
            set prev = next.prev
            set next.prev = this
            set prev.next = this
        endmethod
       
        private ability abil
        private integer abil_raw
       
        method getMaxLevel takes nothing returns integer
            debug if this == 0 then
                debug set this = 1/0
            debug endif
            return GetAbilityMaxLevel(abil_raw)
        endmethod
       
        method getCurManacost takes integer level returns integer
            local integer maxLevel = getMaxLevel()
           
            static if DEBUG_MODE then
                if this == 0 then
                    set this = 1/0
                endif
                if level < 0 then
                    set level = -level
                endif
            endif
           
            if level > maxLevel then
                return GetAbilityManaCost(abil_raw, maxLevel)
            endif
            return GetAbilityManaCost(abil_raw, level)
        endmethod
       
        method getCooldown takes nothing returns real
            debug if this == 0 then
                debug set this = 1/0
            debug endif
            return GetAbilityCurrentCooldown(abil)
        endmethod
       
        static if LIBRARY_Hashtable then
            //! runtextmacro s__iterations("", "operator []", "ability", "abil")
        else
            static method operator [] takes ability which returns thistype
                local thistype this = thistype(0).next
                loop
                    exitwhen this == 0 or this.abil == which
                    set this = this.next
                endloop
                return this
            endmethod
        endif
       
        static method create takes ability which, integer raw returns thistype
            local thistype this = thistype[which]
            if this == 0 then
                set this = allocate()
                set this.abil = which
                set this.abil_raw = raw
                call this.push()
            endif
            return this
        endmethod
    endstruct
   
    private function Cond takes nothing returns boolean
        call Ability.create(GetSpellAbility(), GetSpellAbilityId())
        return false
    endfunction
   
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Filter(function Cond))
        set t = null
    endfunction
endlibrary
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
It's done, GetMouseX/Y/Z functions were ported from Memory Hack and are working correctly in patches 1.27b and 1.28. Check the 1st post and get updated versions of libraries Memory, Version and Utils
Thank you, tested and it works in 1.27b, can't really try in 1.28 yet. Btw you left a debug mesage in Memory lib init.
Anyway, can you just make an in-depth noob-friendly tutorial on how to deal with memory stuffs? I mean things like finding memory offsets, etc. So that we can use this for our needs without having to bother you all the time. I'm quite interested to learn these stuffs. :)
 
Level 3
Joined
May 19, 2010
Messages
35
Now that patch 1.28.1 is around and all offsets in the version library are broken again :hohum: I have a little suggestion for that library.
If there are no known offsets for the current game version, then try to read them from the hdd.
JASS:
local integer version = Memory[GetBytecodeAddress()/4]
set version = version - Memory[version/4]
if version == 2895996 then
    // init offsets for version
<snip>
else
    call Preloader("version_offsets_file.txt")
    // setup offsets from file
endif
That way a map can still work with memory even after a new patch, even when the original author is no longer around to update the map. Once the new offsets are figured out all I need to do is patch that file and all maps work again.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,180
Now that patch 1.28.1 is around and all offsets in the version library are broken again :hohum: I have a little suggestion for that library.
Which is what I have been saying all along...

That way a map can still work with memory even after a new patch, even when the original author is no longer around to update the map. Once the new offsets are figured out all I need to do is patch that file and all maps work again.
Arbitrary virtual memory reading will eventually be patched away completely. There is no long term maintainable way to use this.
 

MindWorX

Tool Moderator
Level 20
Joined
Aug 3, 2004
Messages
709
Why would Blizz patch something that do no damage to the game? Other than f***ing up the replays
Consider the fact that your CD-Key is loaded up and validated at multiple points. When you start the game and when you connect to battle.net. This means that the whole key exists in memory at some point or other. And when you connect to battle.net, your user credentials are also stored in plain sight in memory. If people can make maps to expose CD-Keys, Blizzard will be in trouble.
 
Level 9
Joined
Jul 30, 2012
Messages
156
Consider the fact that your CD-Key is loaded up and validated at multiple points. When you start the game and when you connect to battle.net. This means that the whole key exists in memory at some point or other. And when you connect to battle.net, your user credentials are also stored in plain sight in memory. If people can make maps to expose CD-Keys, Blizzard will be in trouble.

Even if it is really possible for a map to find the user's CD key in memory, they could do nothing with it. It's impossible for a map to retrieve the CD key and send it to someone else, because the map script has no access to internet. Also I believe that CD-key doesn't stay in memory, I think it's checked only when the game opens.
 
Level 6
Joined
Jul 30, 2013
Messages
282
You could can extract a bunch of stuff if you host a bot tho, all you need to do is make sure the information is sent to another player at listen in.
getting nuff people to play on your evil bot would make it probably not worth doing for most ppl, but it can be done if you really want. (IF you can read the keys..)
 
Level 5
Joined
Jun 16, 2004
Messages
108
If one was able to grab the cd key from memory, one could send it through the sync natives. The map could be designed to send such information only after some form of authentication, and of course it could be done without any visible signs to victims. Using the sync natives also puts it in the replay stream if I recall, which could allow for a setup involving host bots and the replays they generate.
 
Level 6
Joined
Jul 30, 2013
Messages
282
there was like w3mmd or sth.. some ghost format for passing data to the bot via replays. not sure how flexible but you could basically do sth like that. or maybe even use that like replace player names with cd-keys for example :p
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,180
Even if it is really possible for a map to find the user's CD key in memory, they could do nothing with it. It's impossible for a map to retrieve the CD key and send it to someone else, because the map script has no access to internet.
When playing online it does. It could send it to the host robot, which could be spoofing the name of a reliable host robot source.

Also I believe that CD-key doesn't stay in memory, I think it's checked only when the game opens.
Is it deleted from memory? Normal memory allocation does not zero out deallocated memory to save time, after all it is correct programming practice to initialize all memory upon allocation anyway. Since the deallocated memory might still be mixed with in use pages it is possible that it still can be read by the application without causing a memory access exception. Once read it could be relayed to a host robot for being stored and stolen.

With C++ when dealing with passwords or other sensitive information you are meant to zero the buffers out upon deallocation to make sure they leave no footprint in the memory for people to read. Java and languages with similar memory model should not suffer from such a problem as arbitrary memory reading is not permitted by the language and is considered a critical security exploit so patched ASAP.
 
Level 9
Joined
Jul 30, 2012
Messages
156
When playing online it does. It could send it to the host robot, which could be spoofing the name of a reliable host robot source.


Is it deleted from memory? Normal memory allocation does not zero out deallocated memory to save time, after all it is correct programming practice to initialize all memory upon allocation anyway. Since the deallocated memory might still be mixed with in use pages it is possible that it still can be read by the application without causing a memory access exception. Once read it could be relayed to a host robot for being stored and stolen.

With C++ when dealing with passwords or other sensitive information you are meant to zero the buffers out upon deallocation to make sure they leave no footprint in the memory for people to read. Java and languages with similar memory model should not suffer from such a problem as arbitrary memory reading is not permitted by the language and is considered a critical security exploit so patched ASAP.

I see why your name is "Dr. Super Good". You really like to be the politically correct guy.

Well, I can't say that your arguments are wrong. If something like the Java VM, that is used by millions of applications worldwide, had a vulnerability that allowed memory reading, it would surely be a critical issue that would have to be patched immediately.

But this is not the Java VM. This is fucking Warcraft III. A 15-year old game, played by "almost nobody", if you compare it with other popular games, and even with itself in the past. A game with a very old and limited engine, and when someone finds a way to remove a small bit of those limitations, we even have to hear people complaining about safety.

Man, seriously, DO YOU REALLY THINK SOMEONE WILL TAKE THE JOB TO MAKE A MAP JUST TO STEAL CD KEYS? IN 2K17? First, it may probably not be possible at all. I'm not gonna take my time to research about this, and I'm pretty sure no one will. Second, it will certainly not be trivial, it may possibly be in a dynamically allocated memory area instead of a fixed address, and that would require scanning the memory for patterns in order to find it, which will probably crash the game before finding valid data, since Jass VM has no exception handling.

And third, if we ignore all these obstacles, WHO IS GONNA TAKE THIS JOB JUST TO STEAL USER'S CD KEYS? Warcraft III is so fucking cheap, and we even have random CD Key generator in the web, of course those keys don't work in bnet, but there are plenty of other places to play WC3 online.

The amount of security in an application is proportional to its size, a game like WC3 doesn't need as much security as something big like Java, especially when these "security measures" will take away useful features from the game, something WE REALLY DON'T NEED considering how limited the engine is already.

When the original Memory hack was released (the full version with write access), the community has been split in two. Those that wanted to use that power for map development, and those that saw it as a critical issue that should be patched. After it got patched by 1.27b, the community has been split once again: those that are happy with read-only access, and those who still think we should go back to the no-returnbug era.

So now there are basically 3 groups in the WC3 community around the world:
  1. The group that wants to stay on older patches, because they don't fucking care about new patches that bring more harm than good. Most of this group uses 3rd party addons for WC3, like W3Arena, iccup, and the chinese dota that uses Lua engine, so they don't need Blizzard to do their shit for them. This group also includes the new generation of maps that use the full Memhack, like DracoL1ch's Dota.

  2. The group that wants to stay on the latest patches and play a safe game, but also want some new cool features that Blizzard has never given us, and probably never will. The read-only memhack is targeted at this group

  3. The "Dr Super Good" group.
I don't need to say that splitting the player base brings no good for the life of WC3. Blizzard has just forgotten about WC3 for years, and people didn't want to wait for them so they made 3rd party addons. Now these new patches are fucking with these addons, and they are giving nothing in exchange.

If read-only memhack is removed there will be again a community split, as the mapmakers that are using it and their playerbase will simply stop updating. They might even migrate back to 1.26 since it's quite stable and gives the full power of memhack.

There's no logical reason to take this decision. Fixing the write exploit was a necessity because it allowed arbitrary code execution. But the read-only mode allows to do what? Stealing CD Keys? Really, removing the ability to read memory, without giving anything in exchange (and what could possibly replace this power?), is nothing but bad for everyone.

Btw, I just had a glimpse: why not implement a "safe-mode" of memory hack? I mean, an option where the player could explicitly give permission for a map to access the memory? We could have something like a "Trusted" folder inside the maps folder, and only maps manually placed on this folder by the player will have special privileges. This way a player could download their favorite map from a trusted source and play with the full power of memhack without worrying about security. Maps downloaded via bnet will never go into this folder of course. And if you want more safety they could even display a message to the user before running a map from the "trusted" folder.
 
Level 5
Joined
Sep 6, 2010
Messages
91
Buying a key now does not require a minimum email account ?.
What is the point, increase the number of keygen systems for an XD game?. Not really!, stop looking for something bad to this, since as leantrop said, there is 1.26 as a patch and 1.28.1 on (protected). Nothing more lol.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,180
The amount of security in an application is proportional to its size, a game like WC3 doesn't need as much security as something big like Java, especially when these "security measures" will take away useful features from the game, something WE REALLY DON'T NEED considering how limited the engine is already.
Wc3 used to be capable of arbitrary code execution. Someone made a fake DotA map which deleted Warcraft III and possibly even damaged the Windows folder if running as administrator.
 
Level 6
Joined
Jul 30, 2013
Messages
282
And that is not possible anymore, so I'd say the game is fine as it is now.
... that we know of.

its quite hard to make a complex c or c++ program truly safe, its quite plausible there are still some exploits waiting to be uncovered.(ex JASM could possibly emit some exotic bytecode seq not otherwise possible that exposes a vulnerability)

And reading arbitrary memory is a lesser capability than executing arbitrary code.
we already can read a subset of memory, its not that farfetched to soppose that subset could be expanded to include the location the CD-key is stored in.

If such a way were to be found we can certainly expect a prompt response from blizzard to minimise the damage.
 

Deleted member 219079

D

Deleted member 219079

@leandrotp I want to know the render viewport extension trick as well. E.g. for Ice Escape the bottom UI is unnecessary, if not for abilities (which are cast exclusively with hotkeys).
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
If the current state is read-only, then you can't do the UI hack.
That's what I thought.
His map will never work with more recent patches. It requires the ability to write stuffs to memory using the well known memory hack, which has been disabled in newer patches....

But...
Will you update dota after 1.28? - DotA Allstars
He's updated it to work on 1.28. It probably means it's still possible to write stuffs in recent patches.
Or... he's doin it by removing UI feature. Don't know, never checked the map.
 
what's the memory offset to hide black borders behind UI?

There's a function at 0x629490 of game.dll which sets the position of a frame. Using this I could hide the black bars behind the UI as well as re-position / hide most UI elements.

Here's how I have it working in my code:

C++:
int __fastcall HookSetFramePosition(int a1, int a2, int x, int y) {

    if (y == -1130113270 || y == 1040522936) {
        y = 0;
    }

    return RealSetFramePosition(a1, a2, x, y);
}
 

Attachments

  • noui2.png
    noui2.png
    4.1 MB · Views: 247
Last edited:
There's a function at 0x629490 of game.dll which sets the position of a frame. Using this I could hide the black bars behind the UI as well as re-position / hide most UI elements.

Here's how I have it working in my code:

C++:
int __fastcall HookSetFramePosition(int a1, int a2, int x, int y) {

    // 1.28.1
    if (y == -1130113270 || y == 1040522936) {
        y = 0;
    }
    // 1.26
    else if ((float)y == -0.02f || (float)y == 0.03f){
        y = 0;
    }

    return RealSetFramePosition(a1, a2, x, y);
}


wow! can you please elaborate how to use it? where to paste the function?
 
wow! can you please elaborate how to use it? where to paste the function?

I don't have the JASS code to do this in 1.26, but I have a custom launcher which can do it.

Download War3Loader.zip and extract the contents to your Warcraft III folder (alongside war3.exe and game.dll).

Features (Works on 1.26 and 1.28.1)
  • Works for any graphic renderer that the game uses (D3D8/D3D9/OGL).
  • Remove black bars from behind the UI. This allows you to completely hide the UI with a transparent texture.
  • Widescreen fix. The game view is no longer stretched and you can see more on the screen.
  • JASS operation limit increased by x1000.
  • Custom JASS natives.
It comes with some basic natives for benchmarking.

JASS:
native StopWatchCreate takes nothing returns integer
native StopWatchTicks takes integer swid returns integer
native StopWatchDestroy takes integer swid returns nothing
There are no configuration options and everything is as-is.

How I specifically removed black bars is reveled in the C++ code in my above post.
 

Attachments

  • screenshot1.png
    screenshot1.png
    4.5 MB · Views: 204
  • screenshot2.png
    screenshot2.png
    2.3 MB · Views: 179
  • War3Loader.zip
    172.7 KB · Views: 139
Last edited:

~El

Level 17
Joined
Jun 13, 2016
Messages
551
I don't have the JASS code to do this in 1.26, but I have a custom launcher which can do it.

Download War3Loader.zip and extract the contents to your Warcraft III folder (alongside war3.exe and game.dll).

Features (Works on 1.26 and 1.28.1)
  • Works for any graphic renderer that the game uses (D3D8/D3D9/OGL).
  • Remove black bars from behind the UI. This allows you to completely hide the UI with a transparent texture.
  • Widescreen fix. The game view is no longer stretched and you can see more on the screen.
  • JASS operation limit increased by x1000.
  • Custom JASS natives.
It comes with some basic natives for benchmarking.

JASS:
native StopWatchCreate takes nothing returns integer
native StopWatchTicks takes integer swid returns integer
native StopWatchDestroy takes integer swid returns nothing
There are no configuration options and everything is as-is.

How I specifically removed black bars is reveled in the C++ code in my above post.

Is there any kind of thread/place for this resource where it belongs?
The widescreen fix is really tempting, and I would like to keep using it, but as with everything that messes with WC3, it tends to break with each update.
Also, oh god, this is really funky:
PVxat0O.jpg
 
Is there any kind of thread/place for this resource where it belongs?
The widescreen fix is really tempting, and I would like to keep using it, but as with everything that messes with WC3, it tends to break with each update.
Also, oh god, this is really funky:

I mainly posted it for the people interested in hiding the UI. Another user posted a widescreen fix in the form of .mix file, so I would suggest that if all you want is widescreen.

[RenderEdge] Widescreen Fix

On a side note, I have successfully re-positioned UI elements (see screenshot).
 

Attachments

  • minui.png
    minui.png
    2.9 MB · Views: 288

~El

Level 17
Joined
Jun 13, 2016
Messages
551
I mainly posted it for the people interested in hiding the UI. Another user posted a widescreen fix in the form of .mix file, so I would suggest that if all you want is widescreen.

[RenderEdge] Widescreen Fix

On a side note, I have successfully re-positioned UI elements (see screenshot).

Wow, this is really neat (the screenshot).

Do you know of a fix that works with the most recent WC3 version? The RenderEdge one doesn't work with 1.28.1

I really hope Blizzard just includes proper widescreen rendering in WC3 at some point.
 
Wow, this is really neat (the screenshot).

Do you know of a fix that works with the most recent WC3 version? The RenderEdge one doesn't work with 1.28.1

I really hope Blizzard just includes proper widescreen rendering in WC3 at some point.

THW is not letting me edit my old post so I'm going to upload to this one.

Download

I attached an updated version of the launcher I previously posted. This one has a config.ini file where you can toggle some settings. I also included a demo map which uses the StopWatch natives, as well as a plugin folder. Beware of the lag spike when benchmarking with an increased JASS operation limit.

Works on 1.26, 1.28.1, and 1.28.2.

Code:
[Main]
ExtendGameView=1
WidescreenFix=1
JASSOperationLimit=100000000
Executable=D:\Games\Warcraft III\war3.exe
 

Attachments

  • War3Loader.zip
    251.4 KB · Views: 184
Status
Not open for further replies.
Top