Posts: 12
Threads: 0
Joined: Dec 2010
Files indeed might be easiest route however you'd need to find space in the MOM.exe for the file writting route and then files will be alot slower then interupts.
Hence the decision to go with interupts rather than with file writting.
January 19th, 2011, 07:41
Posts: 5
Threads: 0
Joined: Jan 2011
The normal DOS way to do this is to create an external terminate-and-stay resident program. DOS has facility on int 0x2F to allow programs to attach themselves to it, using register AH to identify the program when int 0x2F is later called. If the TSR needs to do things without external calling, it also hooks itself to the system timer interrupt. After hooking, the program calls int 0x27/0x21 to terminate and leave itself in memory.
The TSR loads before MoM, hooks itself to the caller interrupts, and returns control to DOS. Then the user loads MoM. Either MoM is patched to call the TSR, or the timer interrupt calls it and the TSR acts when it learns that MoM is loaded. While MoM is running, the TSR can do anything you want it to do. Since it has access to all 16-bit memory, it can find the MoM data structures and manipulate them.
To call that TSR from inside MoM, you load the registers with AH=TSRID, a TSR function code in (typically) AL, data in other registers of your choice, then call int 0x2F. The data can be a pointer to what to operate on, or operational control parameters. Defining a consistent calling protocol is one of the most important parts of the TSR design. A good one will greatly ease the work of patching MoM. Also, the TSR program could have knowledge of the internals of MoM to directly access data and modify execution without any passed data.
The TSR interrupt should call into a dispatcher that sends program control to the right TSR function depending on the TSR function code in AL. One function should be a presence detect that returns an eyecatcher so that a caller can turn off support for the TSR if it is not loaded. Another function could be a setup call that patches MoM in memory to use the TSR. In that case, changing of the MoM EXE itself can be limited to a few places. I'm not sure how this idea would interact with the WIZARDS.EXE overlay functions. A scheme might be needed to patch routines each time they are loaded. A really clever scheme would patch TSR support into the overlay loader, so that overlay functions from another file entirely can be loaded. Of course, what you can do is limited by the structure of MAGIC.EXE, and patching it many places may be the best way.
The great difficulty lies in keeping the TSR program from using too much conventional memory. If too much is used, MoM will not load. There are four usual approaches to conserve memory: have the TSR use code overlays like WIZARDS.EXE is used by MoM, have it page parts of itself away into EMS, have it load some parts high using XMS, or have it use a DOS-extender to directly access memory over 1MB. DOSBOX capability will probably determine the best method.
January 29th, 2011, 13:37
Posts: 12
Threads: 0
Joined: Dec 2010
Yeah i read the same thing. Any idea's on where to get a compiler??? or maybe more info on making a TSR ???
I'm young but i develop in C so i understand the concepts but the tools are quite forgein to me.
January 31st, 2011, 23:45
Posts: 5
Threads: 0
Joined: Jan 2011
GuyverThree Wrote:Yeah i read the same thing. Any idea's on where to get a compiler??? or maybe more info on making a TSR ???
I'm young but i develop in C so i understand the concepts but the tools are quite forgein to me.
Borland C++ V2/V3 are the ones to use for your skill set. You'll have to use a little inline assembler to set up the interrupt service, but most of that can be copied from the examples. You'll have to run it in DOSBOX, or real DOS, of course.
The compilers are available as abandonware, or from the Borland Museum if it is working. Note that V4 and V5 are primarily for 32 bit Windows, and so are harder to use for 16 bit programs.
A good reference for calling interrupts and making ISRs is Atkinson's Using Borland C++. There is a different version of the book for each major compiler version. I see used copies available at amazon for basically shipping cost.
Detailed information on DOS interrupts can be found by Googling Ralf Brown's Interrupt List.
Some discussion of how to terminate a program with int 27 in C++ is here...
http://www.digitalmars.com/rtl/tsr.html
A very short, simple ASM TSR example is here:
http://groups.google.com/group/net.micro...5cce247c51
February 4th, 2011, 10:27
Posts: 104
Threads: 0
Joined: Oct 2010
I fail to understand why to make things so complicated with your interrupts, when you could simply edit and change the memory of Master of Magic externally.
For instance, Microsoft provides Windows program developers full set of functions, which allow the modification and tampering of other programs. It is really pretty straight forward to manipulate any accessible process with those functions.
Window functions
Debugging functions
Process and Thread Functions
But ya if interrupts do work with little effort, why not
PS. I would not recommend the Borland C++ compiler for the beginners. If you want to learn C++, you must start with the standard library, which is the essential part in C++. Try googling guides and tutorials in google. Open source C++ compiler, G++ is suitable for beginners and experts. Below are two good and active IDE's, which both include GCC as a compiler.
http://www.codelite.org/
http://www.codeblocks.org/
February 4th, 2011, 16:07
Posts: 5
Threads: 0
Joined: Jan 2011
Jtm Wrote:I fail to understand why to make things so complicated with your interrupts, when you could simply edit and change the memory of Master of Magic externally.
[...]
But ya if interrupts do work with little effort, why not 
Master of Magic is a DOS program, that runs in a DOS emulator. An easy(1) attack is to use standard, well known DOS methods in the emulated DOS space. The standard method for externalizing under DOS is to make a TSR.
Another easy(1) attack is to try to get at it from Windows. There are three difficulties from inside Windows:
1. Normal Windows processes cannot see other processes' memory spaces. So you can't just edit another processes' memory.
2. MoM is a 16 bit program and the Windows API is 32/64 bit. The MoM 'virtual DOS machine' cannot call any Windows API directly, nor share any memory directly with it.
3. MoM is a game. Interrupting screen drawing and sound routines will cause it to crash.
So, a Windows program running in 32 bit mode has to penetrate the emulator memory space and insert control flow transfers from 16 to 32 bit mode without screwing up DOSBOX.
The normal way of doing that would be to:
- Inject your code into DOSBOX's process space
- Find out where MoM lives in that process space
- Set up some a facility so you can halt the MoM program, run your code, and restart MoM at need.(2)
- Patch MoM's 16 bit process with the instructions to bypass its own AI(3)
- Set up some kind of 16 to 32 bit thunker so you can find and pass data back and forth between 32 bit space and 16 bit pointers(4)
Not all that hard, but weird and messy. kyrub posted somewhere that he was able to find MoM inside DOSBOX pretty easily.
A Windows programmer interested in such a project can start here: http://www.codeproject.com/KB/threads/winspy.aspx
Note (1) Easy is compared to hand hex editing ASM code like kyrub has been doing
Note (2) Several proposals on synchronization were made up above. Some of them are quite workable once you get your code running inside DOSBOX's memory space.
Note (3) You could NOP out functions in WIZARDS.EXE rather than patching it 'live'. The downside is that you have to match versions of your AI and versions of modified MoM. The upside is that you don't have to handle MoM constantly pulling the table out from under you by swapping out overlays.
Note (4) I don't know enough about how DOSBOX works to say how hard it is to access its 16 bit code space from inside its 32 bit Windows process. Ickus seems to have some negative ideas about it in a previous post.
Jtm Wrote:PS. I would not recommend the Borland C++ compiler for the beginners. If you want to learn C++, you must start with [url="http://www2.roguewave.com/support/docs/sourcepro/edition9-update1
GuyverThree said he is a C developer. The Borland compilers have four advantages for the DOS task:
1. They can handle both K&R C and inline 16 bit Assembler
2. They run under DOS and produce native DOS code, which none of your links do.
3. They have excellent DOS libraries, including the int86 functions
4. MoM was written with one of them, so making call-compatible code should be a matter of picking the right memory model at compile time
February 6th, 2011, 03:51
Posts: 104
Threads: 0
Joined: Oct 2010
davidtaylornc Wrote:Another easy(1) attack is to try to get at it from Windows. There are three difficulties from inside Windows:
1. Normal Windows processes cannot see other processes' memory spaces. So you can't just edit another processes' memory.
2. MoM is a 16 bit program and the Windows API is 32/64 bit. The MoM 'virtual DOS machine' cannot call any Windows API directly, nor share any memory directly with it.
3. MoM is a game. Interrupting screen drawing and sound routines will cause it to crash. 1. What is normal? If its feasible, then it is. There are set of PSAPI functions which allow the precise memory mapping of any accessible process. These and other functions with some knowledge of DOSBox's internals can be used to locate Master of Magic inside DOSBox memory. This is what I and ILSe (I Like Serena) do in our programs.
2. True. However, for memory hacking case this is not relevant issue because all MoM does is loop and wait a status bit to change. MoM does not know there is anything happening or accessing it memory. When the bit changes, MoM continues. ILSe actually sent me modified WIZARDS.exe which waits status bit change while in battle mode. The game 'freezes' into the loop until my program changes the bit.
3. No need for such thing and does not concern us. We'd be editing the data of AI only.
Quote:kyrub posted somewhere that he was able to find MoM inside DOSBOX pretty easily.
The location of MoM inside depends on which OS you are using. In Windows DOSBox, MoM is located at Commit (Private) memory module of size 0x1001000. MoM's data segment zero can then be found by searching string "\0\0\0\0Borland C++ - Copyright 1991 Borland Intl.". For more thorough information on OS differences, ask ILSe.
Quote:A Windows programmer interested in such a project can start here: http://www.codeproject.com/KB/threads/winspy.aspx
Fascinating. Thank you for sharing it
Quote:Note (4) I don't know enough about how DOSBOX works to say how hard it is to access its 16 bit code space from inside its 32 bit Windows process. Ickus seems to have some negative ideas about it in a previous post.
Really depends on what you wish to access. I'm assuming we have no need to modify MoM machine code on the fly. Everything else should be accessible. See for your self or with ILSe's Real-Time Game Tweaker.
Quote:GuyverThree said he is a C developer. The Borland compilers have four advantages for the DOS task:
...
Fair enough. For your case it fits perfectly. My main concern was really GuyverThree learning the actual C++.
February 12th, 2011, 11:15
Posts: 5
Threads: 0
Joined: Jan 2011
Jtm Wrote:1. What is normal? If its feasible, then it is. There are set of PSAPI functions which allow the precise memory mapping of any accessible process. These and other functions with some knowledge of DOSBox's internals can be used to locate Master of Magic inside DOSBox memory. This is what I and ILSe (I Like Serena) do in our programs.
Ok. I did not know about those programs. They seem quite far along. The project does not need this help, so I will stop trying to give it.
Jtm Wrote:2. True. However, for memory hacking case this is not relevant issue because all MoM does is loop and wait a status bit to change. MoM does not know there is anything happening or accessing it memory. When the bit changes, MoM continues. ILSe actually sent me modified WIZARDS.exe which waits status bit change while in battle mode. The game 'freezes' into the loop until my program changes the bit.
3. No need for such thing and does not concern us. We'd be editing the data of AI only.
[...]
Really depends on what you wish to access. I'm assuming we have no need to modify MoM machine code on the fly.
I saw the waitloop code upstream in the thread. I am very surprised that sitting in it does not crash MoM's graphics or sound routines. Perhaps MoM is more robust than many DOS games in this way. That makes things easier.
The thread seemed to get stuck due to some limitations of wizards.exe structure in NOPping out AI and using waitloops. It seemed like it needed an idea how to add some code inside DOS to save space. So, interrupts and a TSR.
I agree there is not really a need to do it that way. You can duplicate any function inside MoM in your program. So you could NOP out huge sections of wizards.exe and use that space to overcome the wizards.exe limitations.
August 18th, 2012, 14:41
(This post was last modified: August 22nd, 2012, 00:57 by Jtm.)
Posts: 104
Threads: 0
Joined: Oct 2010
Hey, it is time to revive this subject  Oh and btw huge thanks to kyrub and ILSe for researched IDA files  Without your work this wouldn't have been possible.
I've made a demo external ai for combatting. You must run MoM under DOSBox. Demo is user controlled. With 'attack <user_id>' command, you can direct the ai units and attack anyone, even ai's own units.
Below is link to the demo and modified wizards.exe:
http://www.smallfiles.org/download/1853/...o.zip.html
Wizards.exe is modified so that without my program combat runs as if nothing is changed, using original ai. But if you run my program, combat would, in real time, change to wait my program. Also, if you exit my program using exit-command during the combat, the original ai is again in use.
EDIT: Known bug: Terminating my program by any other way than 'exit' command will not change COMBAT back to original ai's control.
BEHIND scenes:
I've changed the first section of the Unit_strategy_exe() subroutine.
Original code in VANILLA:
Code: ; Start of Unit_strategy_exe() routine
; WIZARDS.EXE:889E4
[COLOR=DarkOrange]
8B C6 mov ax, si_casting_battle_unitID
BA 6E 00 mov dx, 6Eh ; 'n'
F7 EA imul dx
C4 1E 2A 92 les bx, addr_Battle_unit
03 D8 add bx, ax
26 8A 47 43 mov al, es:[bx+43h]
98 cbw
BA 6E 00 mov dx, 6Eh ; 'n'
F7 EA imul dx
C4 1E 2A 92 les bx, addr_Battle_unit
03 D8 add bx, ax
26 8B 7F 44 mov di, es:[bx+struc_Battle_Unit.field_xPos]
8B C6 mov ax, si_casting_battle_unitID
BA 6E 00 mov dx, 6Eh ; 'n'
F7 EA imul dx
C4 1E 2A 92 les bx, addr_Battle_unit
03 D8 add bx, ax
26 8A 47 43 mov al, es:[bx+struc_Battle_Unit.field_target_battle_unitID_??]
98 cbw
BA 6E 00 mov dx, 6Eh ; 'n'
F7 EA imul dx
C4 1E 2A 92 les bx, addr_Battle_unit
03 D8 add bx, ax
loc_9251A:
26 8B 47 46 mov ax, es:[bx+struc_Battle_Unit.field_yPos]
89 46 0A mov [bp+target_Y], ax
[/COLOR]
Replacing the orange code with the green code.
Code: ; Start of Unit_strategy_exe() routine
; WIZARDS.EXE:889E4
[COLOR=YellowGreen]
55 push bp ; auto combat
8B EC mov bp, sp
83 EC 06 sub sp, 6
56 push si_casting_battle_unitID
57 push di
8B 76 06 mov si_casting_battle_unitID, [bp+casting_battleUnitID]
8B C6 mov ax, si_casting_battle_unitID
BA 6E 00 mov dx, 6Eh ; 'n'
F7 EA imul dx
C4 1E 2A 92 les bx, addr_Battle_unit
03 D8 add bx, ax
26 8A 47 43 mov al, es:[bx+43h]
98 cbw
BA 6E 00 mov dx, 6Eh ; 'n'
F7 EA imul dx
C4 1E 2A 92 les bx, addr_Battle_unit
03 D8 add bx, ax
26 8B 7F 44 mov di, es:[bx+struc_Battle_Unit.field_xPos]
26 8B 47 46 mov ax, es:[bx+struc_Battle_Unit.field_yPos]
89 7E 0A mov [bp+target_Y], di
89 46 08 mov [bp+target_X], ax
50 push ax
A0 97 92 mov al, [w_kyrub_dseg_9297]
08 C0 or al, al
74 18 jz loc_no_external_AI:
B0 01 mov al, 1
A2 96 92 mov [w_kyrub_dseg_9296], al
loc_wait_external_AI:
A0 96 92 mov al, [w_kyrub_dseg_9296]
08 C0 or al, al
75 F9 jnz loc_wait_external_AI
A8 00 89 mov ax, [w_kyrub_dseg_8900]
89 46 08 mov [bp+target_X], ax
A8 02 89 mov ax, [w_kyrub_dseg_8902]
89 46 0A mov [bp+target_Y], ax
loc_no_external_AI:
58 pop ax
90 90 90 90 90 90
[/COLOR]
Notice the flag in dseg:9297. By default it is 0, meaning the external ai is disabled. It's purpose is choose whether to use the original ai or wait for the external program. My program changes dseg:9297 flag to 1 when it executes. When dseg:9297 = 1, then MoM waits until dseg:9296 is reset to zero. My program saves x- and y-coordinates to dseg:8900 and dseg:8902.
Code: Replace opcodes in 0x889E4
[color=DarkOrange]558BEC83EC0656578B76068B7E080BFF754B837E0A0075458BC6BA6E00F7EAC41E2A9203D8268A474398BA6E00F7EAC41E2A9203D8268B7F448BC6BA6E00F7EAC41E2A9203D8268A474398BA6E00F7EAC41E2A9203D8268B474689460A[/color]
with
[color=YellowGreen]558BEC83EC0656578B76068BC6BA6E00F7EAC41E2A9203D8268A474398BA6E00F7EAC41E2A9203D8268B7F44268B4746897E0A89460850A0979208C07418B001A29692A0969208C075F9A80089894608A8028989460A58909090909090[/color]
Posts: 104
Threads: 0
Joined: Oct 2010
The purpose of the demo program is to show that programming an external ai is possible. But this is still very limited. So far I've only been able to change attacker's target.
More details about the demo:
1. Demo checks the status flags every 10 ms, which I believe is quick enough. CPU usage is still less than 1 %.
2. In addition to saving x and y to 8900 and 8902, the program also changes attacker's battle unit data at +0x43, which is the location for the target unit id. It is required.
Limitations:
So far I've failed to implement a working code for simply moving the attacker to next tile. I've tried to change code so that Master of Magic calls j_Move_or_attack_reaction subroutine after picking up the coordinates from external ai. This subroutine is used when player moves units with mouse pointer. So far failed. If anyone is interested, I can send the alternative code that implements the call to j_Move_or_attack_reaction.
EDIT: more details
|