wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Tutorials > Misc. Tutorials
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 06-18-2006, 08:28 PM   #1
PipeDream
Moderator
 
PipeDream's Avatar


Code Moderator
 
Join Date: Feb 2006
Posts: 1,405

Submissions (6)

PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)

Default Adding your own Natives to warcraft III (c/c++)

In this tutorial I'm going to show how to use xttoc's jAPI to write your own warcraft natives. You'll need to know C/C++ and have a compiler whose calling convention you know (IE, can get lucky with). I'll be using borland's bcc 5.60. Unfortunately I couldn't get anything free to work.

Theoretically with this technique we'll be able to do anything at all, although it may take quite a bit of research. Even then there'll be a serious limitation in that maps won't be bnet distributable. I'd like to specifically warn against someone writing a general library that maps could call, as this would be begging for security problems.

In any case, let's get started. One thing that would be particularly nice to have is C routines for data structures. We'll implement two: One, a heap- just dynamically allocated arrays. Then we'll do something a little fancier, a disjoint set for mazes.
First off grab jAPI Tool from this thread:http://www.wc3campaigns.net/showthread.php?t=79652. Stuff all the files into your warcraft3 directory except for the jNatives folder-keep the folder but toss its contents. You'll probably want shortcuts for both the loaders, in particular, add the -window flag to the LoaderWar3.exe for testing, as in target: "D:\games\Warcraft III\LoaderWar3.exe" -window

The outline of what needs to happen is:
- Write a native
- Load native into warcraft
- Get custom declaration of native into common.j
- Write code that uses native
- Run game

We'll start with just a hello world. Here's our test.cpp:
Code:
/*
The following routines are all exported by japi.dll:

char* MemStrSearch(unsigned char *start, unsigned char *end, char *str);
bool MemPatternCompare(unsigned char *address, unsigned char *pattern, unsigned char *mask, unsigned long length);
unsigned char* MemPatternSearch(unsigned char *start, unsigned char *end, unsigned char *pattern, unsigned char *mask, unsigned long length);

PIMAGE_SECTION_HEADER GetImageSectionHeaders(HMODULE hModule, WORD *count);
PIMAGE_SECTION_HEADER GetImageSectionHeader(HMODULE hModule, unsigned char name[8]);

void	jAPI jBindNative(void *routine, char *name, char *prototype);
void	jAddNative(void *routine, char *name, char *prototype);
jString jAPI jStrMap(char *str);
char*	jAPI jStrGet(jString strid);
*/
// - Andy Scott aka xttocs


#include <windows.h>
#include <stdio.h>

#define jNATIVE	__stdcall
#define jAPI	__msfastcall

#define FloatAsInt(f) (*(long*)&f)
#define IntAsFloat(i) (*(float*)&i)

typedef long jString;
typedef long jInt;
typedef long jReal;

typedef void	(jAPI *jpAddNative)(void *routine, char *name, char *prototype);
typedef jString (jAPI *jpStrMap)	(char *str);
typedef char *	(jAPI *jpStrGet)	(jString strid);

jpAddNative		jAddNative;
jpStrMap		jStrMap;
jpStrGet		jStrGet;

#pragma warning ( disable : 4996 )


jString jNATIVE test(jString js, char *fnname)
{
	char *s;
	s = jStrGet(js);
	s[0] = 'A';
	return jStrMap(s);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	if (ul_reason_for_call == DLL_PROCESS_ATTACH)
	{
		DisableThreadLibraryCalls(hModule);

		HMODULE hjApi = GetModuleHandle("japi.dll");

		jAddNative	= (jpAddNative)GetProcAddress(hjApi, "jAddNative");
		jStrMap		= (jpStrMap)*GetProcAddress(hjApi, "jStrMap");
		jStrGet		= (jpStrGet)*GetProcAddress(hjApi, "jStrGet");

		jAddNative(test, "test", "(S)S");
	}
    return TRUE;
}

The two important bits here are:
Code:
jString jNATIVE test(jString js, char *fnname)
{
	char *s;
	s = jStrGet(js);
	s[0] = 'A';
	return jStrMap(s);
}
and
Code:
		jAddNative(test, "test", "(S)S");
If stuff crashes and you're not using borland's compiler, try using __fastcall instead of __msfastcall.
Our test function takes a string and returns a string. It simply substitutes the first character in the string for the letter 'A', hoping that it wasn't passed a string of zero length. The engine also passes the name of the called function to the native so we include that fnname to make sure the stack gets cleaned up when we're done.
That third argument of jAddNative gives the type information. In parentheses comes the arguments. For example, (ISI) would mean takes integer x, string s, integer y. After the parenthesis comes the return type, in our case, S for string.
We need to compile a DLL. In a command prompt:
Code:
bcc32 -WD -e"test.xjp" test.cpp
-WD means build a dll, -e is the output file and test.cpp is of course the input. The file extension is .xjp instead of .dll because this is what jAPI recognizes and links into warcraft. Now stuff the .xjp file into where jAPI will look, the jNatives folder in your warcraft directory (which should be otherwise empty).
Next we need to add the native to common.j. If you don't have a copy, extract it with your favorite mpq archiver from war3patch.mpq.
Collapse JASS:
native test takes string s returns string
Now start LoaderWorldEditor.exe from the jAPI archive. If nothing at all happens, you may have registry issues. Try reinstalling warcraft. If it crashes, then something is probably wrong with the calling convention you set the functions to. Post the error message and the dll you generated and I can take a look. Now that the editor is running, create a new map and do whatever description fiddling you like. Import your modified common.j and change its base directory from war3mapImported\ to Scripts\ so that it overwrites the default blizzard copy. Save. Usually it'll fail the first time, before common.j imports properly. If after a couple saves you continue to get registered native declaration problems, check that the types you set in common.j match the types you set in the jAddNative call in test.cpp.

If everything is still going OK, write some JASS to try out your new test function. Try running call BJDebugMsg(test("Hello")) a few seconds into the game. Save your map. If the native doesn't seem to be declared, check that your common.j has the prototype line. If it saves ok, don't bother with the test map button. Instead run the jAPI LoaderWar3.exe, preferably through a shortcut with the -window flag. Head over to your map and start it up. If, upon clicking the map in the map selection screen, no player slots appear, you're probably missing some natives in your .xjp file. All the natives you added in common.j must have an entry in your custom dll.

If the game runs and displays "Aello", congratulations, you've written your first custom native. Now let's do something more interesting. One common lament of JASS programmers is the lack of dynamically allocated memory. We can't even pass arrays! In C, however, we can easily malloc() a new chunk of memory. This is a little dangerous, since it won't be cleaned up when the map finishes. We'll have to do it manually for now, but this is good practice anyway. For simplicity, and since we have the return bug for flexibility, we'll just write arrays of integers. So we're going to want four functions:
Collapse JASS:
native ArrayAlloc takes integer length returns integer
native ArrayFree takes integer arr returns nothing
native ArrayGet takes integer arr, integer index returns integer
native ArraySet takes integer arr, integer index, integer val returns nothing
Go ahead and add them to common.j

Next copy test.cpp to array.cpp and replace the test native with some array natives:
Code:
jInt jNATIVE ArrayAlloc(jInt len, char *fnname)
{
	return (jInt)malloc(sizeof(jInt)*len);
}

jInt jNATIVE ArraySet(jInt array,jInt index,jInt val, char *fnname)
{
	int *p = (int *)array;
	p[index] = val;
	return p[index];
}

jInt jNATIVE ArrayGet(jInt array, jInt index, char *fnname) {
	int *p = (int *) array;
	return p[index];
}

jInt jNATIVE ArrayFree(jInt array, char *fnname) {
	int *p = (int *)array;
	free(p);
	return 0;
}
I've had all of them return something since JASS seems to expect some sort of return value from every function, even if it's told that it returns nothing.
To register them, we'll need these lines in DllMain:
Code:
		jAddNative(ArrayAlloc,"ArrayAlloc","(I)I");
		jAddNative(ArraySet,"ArraySet","(III)I");
		jAddNative(ArrayGet,"ArrayGet","(II)I");
		jAddNative(ArrayFree,"ArrayFree","(I)I");

Compile this with bcc32 -WD -e"array.xjp" array.cpp
Toss out your test.xjp and replace it in the jNatives folder with array.xjp.

Try some simple test code, perhaps:
Collapse JASS:
local integer heap = ArrayAlloc(16)
call ArraySet(heap,5,42)
call BJDebugMsg(I2S(ArrayGet(heap,5)))
call ArrayFree(heap)
If you have problems, go back and check the troubleshooting tips listed for 'test'.
Exercise: Add bounds checking and dynamic resizing to your array.

Here's another native, this one's for blu and his maze generation map, a disjoint set:
Code:
typedef struct {
	int parent;
	int rank;
} node;

jInt jNATIVE DJSNew(jInt len, char *fnname)
{
	node *set = (node *)malloc(sizeof(node)*len);
	int i;
	for(i=0;i<len;i++) {
		set[i].parent = -1;
		set[i].rank = 0;
	}
	return (int)set;
}

jInt jNATIVE DJSFind(jInt iset, jInt x, char *fnname) {
	node *set = (node *)iset;
	if (set[x].parent == -1) return x;
	set[x].parent = DJSFind(iset,set[x].parent);
	return set[x].parent;
}

jInt jNATIVE DJSUnion(jInt iset,jInt x,jInt y, char *fnname)
{
	node *set = (node *)iset;
	int xr = DJSFind(iset,x);
	int yr = DJSFind(iset,y);
	if(set[xr].rank > set[yr].rank)
		set[yr].parent = xr;
	else if(set[xr].rank < set[yr].rank)
		set[xr].parent = yr;
	else {
		set[yr].parent = xr;
		set[xr].rank++;
	}
	return 0;
}

jInt jNATIVE DJSFree(jInt iset, char *fnname) {
	free((node *)iset);
	return 0;
}

Or some lists for weaaddar:
Code:
typedef struct {
	int car;
	int cdr;
} pair;

pair *allocpair() {
	return (pair *)malloc(sizeof(pair));
}

void freepair(pair *p) {
	free(p);
}

jInt jNATIVE cons(jInt x, jInt y, char *fnname) {
	pair *newpair = allocpair();
	newpair->car = x;
	newpair->cdr = y;
	return (jInt)newpair;
}

jInt jNATIVE car(jInt ipair, char *fnname) {
	pair *p = (pair *)ipair;
	return p->car;
}
jInt jNATIVE cdr(jInt ipair, char *fnname) {
	pair *p = (pair *)ipair;
	return p->cdr;
}

Prototype letters:
Code:
Integer		I
Real		R
String		S
Code		C
Boolean		B
Returns nothing	V
Handles		H; (?)
Handle ext.	H followed by ext followed by semicolon e.g. Hplayer;

Big thanks to xttocs for doing all the hard work and thanks for reading. If you manage to get this working with different compilers or how to work with handles or anything really, please post what/how! Good luck.

--------
Known good compiler list
Borland 5.60
Borland 5.5 (free)
--------
Important update: Each native needs an additional "char *fnname" argument at the end. Warcraft passes the name of the called native in so we need to make sure the stack is cleaned up. Thanks xttocs.
If you have problems with the stock loader, try the one in loader.rar
Attached Files
File Type: rar testmap.rar (129.8 KB, 221 views)
File Type: rar loader.rar (52.4 KB, 158 views)
__________________
PipeDream is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 06-18-2006, 10:29 PM   #2
MindWorX
Level 1 Neothelid


Tools Moderator
 
Join Date: Mar 2004
Posts: 254

Submissions (2)

MindWorX has a spectacular aura about (147)

Default

Tested, and approved (Not that my approval means much, but tested it, and i know it works)
MindWorX is offline   Reply With Quote
Old 06-19-2006, 01:39 AM   #3
Guesst
User
 
Guesst's Avatar
 
Join Date: Mar 2006
Posts: 301

Submissions (3)

Guesst is a jewel in the rough (185)Guesst is a jewel in the rough (185)Guesst is a jewel in the rough (185)

Default

I got stuck here:
Quote:
Import your modified common.j and change its base directory from war3mapImported\ to Scripts\ so that it overwrites the default blizzard copy. Save. Usually it'll fail the first time, before common.j imports properly. If after a couple saves you continue to get registered native declaration problems, check that the types you set in common.j match the types you set in the jAddNative call in test.cpp.
It keeps giving me about a hundred script errors about excepting one thing or another when I try to save. I used your test.cpp. Is there something else I could be doing wrong?
Guesst is offline   Reply With Quote
Old 06-19-2006, 01:55 AM   #4
Vexorian
Free Software Terrorist
 
Vexorian's Avatar


Technical Director
 
Join Date: Apr 2003
Posts: 14,898

Submissions (37)

Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)

Hero Contest #3 - 2nd Place

Default

I think that you should also have that common.j in a scripts subfolder of war3's folder.

And make sure you didn't make syntax errors when adding the natives to common.j.


I am talking out of my ass right now, but what if you try to add a: type superarray extends handle
and then make those natives use superarray as argument / return value ?

hmnn I should probably take my mingw32 and try to make this work there and then test the monster myself.

--

I renamed the title, also I moved it to misc tutorials. It is not really a JASS tutorial so it wouldn't fit there
__________________
Zoom (requires log in)Wc3 map optimizer 5.0
Someone should fix .wav sound in this thing.
Zoom (requires log in)JassHelper 0.A.2.A
Turns your simple code into something that is complicated enough to work.
Faster != more useful
Vexorian is offline   Reply With Quote
Old 06-19-2006, 03:53 AM   #5
PipeDream
Moderator
 
PipeDream's Avatar


Code Moderator
 
Join Date: Feb 2006
Posts: 1,405

Submissions (6)

PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)

Default

Getting a little type safety on the arrays would be nice. However I'm pretty happy with integers since we don't have to return bug. I think if we want type safety it should come from a language on top of jass. Speaking of the return bug, you can write proper typecast natives that do the typecasting in C. This'll compile to just move input to output, so zero cost and way faster than the return bug. We can also do it with out understanding handles.
Apparently borland has a free as in beer compiler. Let me know if it requires any tricks.

- attached a test map with all the ingredients to help folks narrow down the source of problems.
- Added prototype information
__________________
PipeDream is offline   Reply With Quote
Old 06-19-2006, 04:27 AM   #6
Vexorian
Free Software Terrorist
 
Vexorian's Avatar


Technical Director
 
Join Date: Apr 2003
Posts: 14,898

Submissions (37)

Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)

Hero Contest #3 - 2nd Place

Default

figured out gcc wouldn't work.

I don't want a free as in beer compiler, I don't want to compile things with propietary compilers, I also had some unfun experiences with borland's vision of c++ but well maybe one of these days I'll try.


I wouldn't have any problem in using integers, but I am corious as if using the type define in common.j would actually work.
__________________
Zoom (requires log in)Wc3 map optimizer 5.0
Someone should fix .wav sound in this thing.
Zoom (requires log in)JassHelper 0.A.2.A
Turns your simple code into something that is complicated enough to work.
Faster != more useful
Vexorian is offline   Reply With Quote
Old 06-19-2006, 02:40 PM   #7
BertTheJasser
xyzi - our universe
 
BertTheJasser's Avatar
 
Join Date: May 2005
Posts: 742

Submissions (2)

BertTheJasser has a spectacular aura about (111)BertTheJasser has a spectacular aura about (111)BertTheJasser has a spectacular aura about (111)BertTheJasser has a spectacular aura about (111)

Default

I am not familiar with c++, so it may take some time till I actually get all the constant keys, but anyways. Great tut! +Rep
__________________
Note: Bye... I had a lot of fun here!
Special thanks to Vexorian who helped me learn jass, the real jass and always helped me when problems occured, I would call him somehow my mentor. Pipedream, who made amazing Grimoire and helped me acclerating my map (currently at 99% finished, no developement atm).

Vote for Linux Ports in general of Blizzard products: http://www.PetitionOnline.com/ibpfl/
BertTheJasser is offline   Reply With Quote
Old 06-19-2006, 03:31 PM   #8
blu_da_noob
Nonchalant
 
blu_da_noob's Avatar


Respected User
 
Join Date: Mar 2006
Posts: 1,933

Submissions (2)

blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)

[Quicksilver #2] - 2nd Place[Quicksilver#1] 1st place

Send a message via MSN to blu_da_noob
Default

Quote:
Originally Posted by PipeDream
Here's another native, this one's for blu and his maze generation map, a disjoint set:

:D

I'll have a look at this stuff when I get back from holiday, but I'm not sure this will ever be useful for general modding, due to the lack of portability (unfortunately :/).
__________________
blu_da_noob is offline   Reply With Quote
Old 06-19-2006, 10:10 PM   #9
Ragnarok X
User
 
Join Date: Dec 2003
Posts: 83

Ragnarok X has little to show at this moment (5)

Default

I can't compile this even changing __fastcall instead of __msfastcall.

I am using Dev-C++ that is a free program, i am having problems with the command prompt, because i don't know what is that and i don't know if Dev-C++ have it.

Please help me.

PD: You think that with borland c++ 5 this work?

Last edited by Ragnarok X : 06-19-2006 at 10:23 PM.
Ragnarok X is offline   Reply With Quote
Old 06-20-2006, 01:47 AM   #10
Vexorian
Free Software Terrorist
 
Vexorian's Avatar


Technical Director
 
Join Date: Apr 2003
Posts: 14,898

Submissions (37)

Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)Vexorian has a reputation beyond repute (1062)

Hero Contest #3 - 2nd Place

Default

Can't understand why would anyone use dev-c++ instead of code::blocks . Anyways it is surelly configured to use gcc (mingw32) as compiler by default, you need to get the borland compiler and then configure dev-c++ to use it instead
__________________
Zoom (requires log in)Wc3 map optimizer 5.0
Someone should fix .wav sound in this thing.
Zoom (requires log in)JassHelper 0.A.2.A
Turns your simple code into something that is complicated enough to work.
Faster != more useful
Vexorian is offline   Reply With Quote
Old 06-20-2006, 04:31 AM   #11
Ragnarok X
User
 
Join Date: Dec 2003
Posts: 83

Ragnarok X has little to show at this moment (5)

Default

Quote:
Originally Posted by Vexorian
Can't understand why would anyone use dev-c++ instead of code::blocks .

Why you say that? (reply this, please)

Anyway thanks for the advice, but how i can change the compiler if i have the borland one?.
Ragnarok X is offline   Reply With Quote
Old 06-20-2006, 07:10 AM   #12
PitzerMike
Alcopops
 
PitzerMike's Avatar


Tools & Tutorials Moderator
 
Join Date: Jan 2003
Posts: 2,794

Submissions (12)

PitzerMike is a splendid one to behold (643)PitzerMike is a splendid one to behold (643)PitzerMike is a splendid one to behold (643)PitzerMike is a splendid one to behold (643)

Approved Map: Pitzer's Minesweeper

Default

You don't have to compile from the IDE if you don't know how to configure it.
You can compile from command line.

Cool tutorial btw.
__________________
Zoom (requires log in)
PitzerMike is offline   Reply With Quote
Old 06-20-2006, 08:59 AM   #13
PipeDream
Moderator
 
PipeDream's Avatar


Code Moderator
 
Join Date: Feb 2006
Posts: 1,405

Submissions (6)

PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)

Default

Quote:
I wouldn't have any problem in using integers, but I am corious as if using the type define in common.j would actually work.
Yep, it works. Neat.
ex:
common.j
Collapse JASS:
//Custom junk
type arr                extends handle
//Dynamic memory
native ArrayAlloc        takes integer len returns arr
native ArraySet            takes arr heap, integer index, integer value returns integer
native ArrayGet            takes arr heap, integer index returns integer
native ArrayFree        takes arr heap returns integer
war3map.j
Collapse JASS:
function Trig_trynative_Actions takes nothing returns nothing
    local arr heap
    call TriggerSleepAction(1.0)
    set heap = ArrayAlloc(16)
    call BJDebugMsg(I2S(HtoI(heap)))
    call ArraySet(heap,0,5)
    call BJDebugMsg(I2S(ArrayGet(heap,0)))
    call ArrayFree(heap)
endfunction
native.cpp
Code:
                jAddNative(ArrayAlloc,"ArrayAlloc","(I)Harr;");
                jAddNative(ArraySet,"ArraySet","(Harr;II)I");
                jAddNative(ArrayGet,"ArrayGet","(Harr;I)I");
                jAddNative(ArrayFree,"ArrayFree","(Harr;)I");

Quote:
Can't understand why would anyone use dev-c++ instead of code::blocks
the code blocks source debugger is rather lacking. regardless I recommend sticking to command line and using a real text editor.

Quote:
I don't want to compile things with propietary compilers
Open watcom should work. If __fastcall isn't microsoft style then you can use a little assembly stub like:
Code:
long addnativewrap(void *routine, char *name, char *prototype) {
 __asm {
  mov ecx,routine
  mov edx,name
  push prototype
  mov eax,jAddNative
  jmp eax //Some compilers don't like jumping to things that aren't labels
 }
}
__________________
PipeDream is offline   Reply With Quote
Old 06-21-2006, 05:41 PM   #14
Ragnarok X
User
 
Join Date: Dec 2003
Posts: 83

Ragnarok X has little to show at this moment (5)

Default

Quote:
Originally Posted by PipeDream
We need to compile a DLL. In a command prompt:
Code:
bcc32 -WD -e"test.xjp" test.cpp

How i can use a command prompt?, how i can acceses to it?, somebody can explain me please?
Ragnarok X is offline   Reply With Quote
Old 06-21-2006, 06:08 PM   #15
PipeDream
Moderator
 
PipeDream's Avatar


Code Moderator
 
Join Date: Feb 2006
Posts: 1,405

Submissions (6)

PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)PipeDream is a glorious beacon of light (463)

Default

I assume you use windows. Start->Run->command.
__________________
PipeDream is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 10:06 PM.


Affiliates
The Hubb The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2018, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services