Author Topic: Nerd's Corner  (Read 79098 times)

Offline Cel

  • Axe Thrower
  • ****
  • Posts: 432
    • View Profile
Re: Nerd's Corner
« Reply #150 on: October 11, 2018, 08:07:56 AM »
Are these sources somewhere on Github? That would be much handier to read/propose changes or additions :-)

That would help having a global view of the thing too.
I do love C++, ASM is readable I just wonder why work with assembly in the first place, it is very low level and tedious to make even small things  ;D.
But hey what ever works for ya is fine  ^-^

The reason is, I checked your archive and it is flagged as malicious by most common anti viruses, they find a trojan-like pattern/signature in the program which may be avoided.
Given the nature and the simplicity of the program (opening a file and parsing binary / creating an image and saving it) I wanted to see if we could try and find a way to fix the issue.

Also I thought if we have it on Github  maybe I and others could try and help you improve the thing by proposing changes (pull requests, merges, branching all that beauty) to add new features to the thing and/or fix track issues.

The virus detection matters because even if we know it is perfectly safe if we want to put it in the downloads section of the site for everyone to find easily, and google runs a basic automated test like they always do on hosted files, they will flag the whole war2.ru site as malicious, as it happened before. And it is a mess after to make them roll back on these things.

Do you think it could be a good idea? :-)

 :critter:

edit: Also it is a shame we do not have the sources of Alexander cech he did something around these lines (PudImage) and Wardraft which was a map editor that allowed a bit more pud manipulations. :-)
« Last Edit: October 11, 2018, 08:20:48 AM by Cel »

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #151 on: October 11, 2018, 10:41:05 AM »
The reason is, I checked your archive and it is flagged as malicious by most common anti viruses,


PFFFFFFFF
^Read this^

If anyone wants to actually put any work into programming I am always the first person to help. I have already spent quite a few hours of my time in reply to your request (YOU'RE WELCOME).

I sometimes publish source to try to help people, but mostly I don't do it for 2 reasons:

   1) Vanity

Most programming jobs involve using personal libraries of code and mashing together various bits that I have been writing my entire life.

To publish the source fpr a specific app I would want to go through it all and cut out all the bits that aren't needed for that app, and make sure that there isn't any noobish trashy things I wrote 20 years ago, and I would be embarrassed to have people see without re-writing them properly.

Also source code often ends up with random notes and comments, non-functional first-attempts at things commented out or not... even notes from phone calls I answered while coding.

It can be very time consuming to check, reorganise and sometimes re-write thousands of lines of code to make it appropriate for public viewing. Just publishing hundreds of source files is like emptying out your underwear draw in a public place.

   2) Why would I bother?

Sorry, but quite honestly I've been publishing a large amount of information about programming and the WC2 client for years now, and I'm yet to see one single line of source code that anyone else has written that has helped me with anything.

I have attempted to collaborate with others before and the only thing that happened is I wasted a heap of hours and nobody else contributed anything.

My first thought when you asked for the source was "sure, why not?"

Then I started looking through all the source files for the 3 different elements of PUDPIC and realised it was probably going to take a couple of days to vet and re-assemble the entire lot into a publishable collection and thought... meh - none of these people ever do anything with any of it, why would I waste my time?

If anyone actually wants to do something with it, there is lots to work with here .... go for it!
If anyone can't ....or more likely can't be bothered, to work out even the small amount I have posted here, then there is no way they will ever do anything useful with the rest.

Want to do some programming? Great... there's a BIG start. How about you post your source here and I will help you with it ok? If you can get you own a version of PUDPIC working I will find a way to make the dlls acceptable... the only issue might be copyright because they contain pretty much all the graphics for the game. :-\


The virus detection matters because even if we know it is perfectly safe if we want to put it in the downloads section of the site for everyone to find easily, and google runs a basic automated test like they always do on hosted files, they will flag the whole war2.ru site as malicious, as it happened before.


This is a valid issue, if anyone running the server asked me about this because they wanted to host my progs, I would try to find a solution. For PUDPIC probably just uncompressing the files might work, idk I havn't even looked at it because nobody has ever suggested that.

Some things I am less comfortable exposing because they work with the WC2 exe and could potentially be exploited for hacks, but PUDPIC only works with pud files so there is no issue there.


Are these sources somewhere on Github? That would be much handier to read/propose changes or additions  .... / snip /.... Do you think it could be a good idea? :-)


This would be really nice. It would mean a lot of work for me, which I would be much more inclined to do if I thought there was any chance at all that anyone else would actually contribute anything, but going on past experience, that is very unlikely.



         ------------------------------

Just while I'm on the subject .... if there is anyone here paranoid enough to actually be concerned about any real malware in any of my stuff, here's a couple of points to ponder:

All you have to do to spot a trojan is firewall it... if its doing anything nasty it will try to phone home... otherwise what's the point?

...and if you actually think that:

a) I am the type of person that would invade people's privacy for my own petty ends
         or
b) that even if I was that kind of person, that I care enough about anyone here's personal life (or even WC2 life) to bother,

.... then the answer is very easy: don't use my programs, and also eat a large bag of dicks :)

its gooder to hax hard and NEVER get caught!

Offline Cel

  • Axe Thrower
  • ****
  • Posts: 432
    • View Profile
Re: Nerd's Corner
« Reply #152 on: October 11, 2018, 12:20:14 PM »
Well that is the thing with Github you dont host the image files nor do you want to host the binary files, just the sources so no copyright issues there because images and the archives are not included  ^-^

You don't have to bother making your sources look good, unless you really want to use these as a way to get noticed by employers or because you want that to be part of your resume or portfolio. <= which also could be quite a good idea but that is indeed more work

Really if not for the above it is most of the time the matter of two clicks.

The benefits are that sometimes external views see things you might have missed.

Most of us have jobs that don't include warcraft 2, but sometimes we take one or two hours to read some foreign code there and there.
Yes nothing may come out of it but sometimes it does.

I guess these names sound familiar to you:
Daniel Lemberg.
Simon Pelsser.
Lasse Jensen.
These dudes worked on analysing the pud format because of their work it is much easier for people like us to play around with it.
Many others like Alexander Cech did programs that did prettymuch the same things like pudImage.exe and the famous Wardraft.exe

Had we got the sources from these utilities for example maybe we wouldn't have to reinvent the wheel every time, just the tires maybe :P

the strength of open source is that even if you let your project  sit out there and stop working on it someone might one day dig it out and use it as a base for something else.

Now that is your choice, in the end you do what you want just saying if you had your sources out there I would have had a look by curiosity :P

:critter:

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #153 on: October 11, 2018, 10:49:14 PM »
nless you really want to use these as a way to get noticed by employers or because you want that to be part of your resume or portfolio. <= which also could be quite a good idea but that is indeed more work

Exactly, and perhaps taking a small amount of pride in my work?
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #154 on: October 13, 2018, 03:20:27 AM »
So for future reference here's a working re-written version of PUDPIC.exe

It draws the terrain and the units.

It doesn't process command line switches or passed filename, just uses the filenames: "this.pud" "this.bmp", and "this.jpg".

I'm sure anyone capable of building it can add those bits if they want.


Lamb.h

Code: [Select]
#include <windows.h>


#ifndef _LAMB_
#define _LAMB_

#define sTYPE 0x45505954 // "TYPE"
#define sVER_ 0x20524556 // "VER "
#define sDESC 0x43534544 // "DESC"
#define sOWNR 0x524E574F // "OWNR"
#define sERA_ 0x20415245 // "ERA "
#define sERAX 0x54415245 // "ERAX"
#define sDIM_ 0x204D4944 // "DIM "
#define sUDTA 0x41544455 // "UDTA"
#define sUNIT 0x54494E55 // "UNIT"
#define sUGRD 0x44524755 // "UGRD"

#define sSIDE 0x45444953 // "SIDE"
#define sSGLD 0x444C4753 // "SGLD"
#define sSLBR 0x52424C53 // "SLBR"
#define sSOIL 0x4C494F53 // "SOIL"
#define sAIPL 0x4C504941 // "AIPL"

#define sMTXM 0x4D58544D // "MTXM"
#define sSQM_ 0x204D5153 // "SQM "
#define sOILM 0x4D4C494F // "OILM"
#define sREGM 0x4D474552 // "REGM"

#define sSIGN 0x4E474953 // "SIGN"
#define sALOW 0x574F4C41 // "ALOW"

#define FOREST     0
#define WINTER     1
#define WASTELAND  2
#define SWAMP      3


#define TILEW  32
#define TILEW2 16


extern "C" {
    typedef struct pudsect {
        DWORD Reserved;
        DWORD name;
        DWORD size;
        LPVOID data;
    }PUDSECT;
}

extern "C" {
    typedef struct hmibmp {
        BITMAPFILEHEADER* pFile;
        BITMAPINFOHEADER* pInfo;
        RGBQUAD*          pPal;
        BYTE*             pBits;
        int               width;
        int               height;
        int               bpp;
        int               linew;
        int               usage;
        int               size;
    }HMIBMP;
}

extern "C" {
    typedef struct pudunit{
    WORD x;
    WORD y;
    BYTE type;
    BYTE owner;
    WORD ai;     // 0-passive 1-active or (gold or oil)/2500
    }PUDUNIT;
}


// LambRES.dll
extern "C" typedef CHAR*   (__stdcall *rfngetname )(int utype);
extern "C" typedef LPVOID  (__stdcall *rfntileset )(int ntileset);
extern "C" typedef LPVOID  (__stdcall *rfngetpal  )(int npalette);
extern "C" typedef LPVOID  (__stdcall *rfngetgrp  )();


//PUDfile.inc
extern "C" typedef LPVOID  (__stdcall *pfnpudload )(CHAR* path);
extern "C" typedef int     (__stdcall *pfnpudsave )(LPVOID hpud,CHAR* path);

//PUDpud.inc
extern "C" typedef int     (__stdcall *pfnsectcnt )(LPVOID hpud);
extern "C" typedef void    (__stdcall *pfndestroy )(LPVOID hpud);
extern "C" typedef LPVOID  (__stdcall *pfndelsect )(LPVOID hpud,LPVOID hsect);
extern "C" typedef PUDSECT*(__stdcall *pfnfindsect)(LPVOID hpud,DWORD sectname);
extern "C" typedef int     (__stdcall *pfnupdsect )(LPVOID hpud,LPVOID hsect,LPVOID lpnew,int nbytes);
extern "C" typedef PUDSECT*(__stdcall *pfngetsect )(LPVOID hpud,int nsect);
extern "C" typedef int     (__stdcall *pfnsectndx )(LPVOID hpud,DWORD sectname);
extern "C" typedef LPVOID  (__stdcall *pfnsectset )(LPVOID hpud,LPVOID hsect,int nsect);
extern "C" typedef BOOL    (__stdcall *pfnsectcre )(LPVOID hpud,DWORD sectname,LPVOID lpnew,int nbytes);
extern "C" typedef LPVOID  (__stdcall *pfnrealloc )(PUDSECT* hSect,int nbytes);

//PUDunit.inc
extern "C" typedef int     (__stdcall *pfnunitsize)(int utype);
extern "C" typedef int     (__stdcall *pfnuniticof)(int utype);
extern "C" typedef int     (__stdcall *pfnlistget )(int list,int ndx);
extern "C" typedef int     (__stdcall *pfnlistnext)(int list,int utype);
extern "C" typedef int     (__stdcall *pfnlistprev)(int list,int utype);
extern "C" typedef LPVOID  (__stdcall *pfnlistname)(int list);

//PUDtile.inc
extern "C" typedef void    (__stdcall *pfnrndrmap )(LPVOID hbmp,LPVOID mtxm,int mapw);
extern "C" typedef void    (__stdcall *pfnrndrtile)(LPVOID hbmp,int tile,int x,int y);
extern "C" typedef LPVOID  (__stdcall *pfngettile )(int tile);
extern "C" typedef void    (__stdcall *pfntileset )(LPVOID tileset);
extern "C" typedef void    (__stdcall *pfnblack   )(HDC dc,int x,int y,int w,int h);

//hmibmp.inc
extern "C" typedef HMIBMP* (__stdcall *pfnmakebmp )(int w,int h,int bpp);
extern "C" typedef void    (__stdcall *pfnfreebmp )(HMIBMP* bmp);
extern "C" typedef int     (__stdcall *pfnzerobmp )(HMIBMP* bmp);
extern "C" typedef void    (__stdcall *pfnsavebmp )(CHAR* path,HMIBMP* bmp);
extern "C" typedef void    (__stdcall *pfnsavejpg )(CHAR* path,HMIBMP* bmp,int quality);
extern "C" typedef void    (__stdcall *pfnperfbmp )(HMIBMP* bmp);
extern "C" typedef void    (__stdcall *pfncopyimg )(HMIBMP* src,int scrx,int srcy,int srcw,int scrh,HMIBMP* dst,int dstx,int dsty,int transp);
extern "C" typedef int     (__stdcall *pfncopyfull)(HMIBMP* dstbmp,HMIBMP* srcbmp,int transp);
extern "C" typedef void    (__stdcall *pfndispimg )(HDC destDC,int x,int y,int w,int h,HMIBMP* bmp,int dx,int dy,int dw,int dh);
extern "C" typedef void    (__stdcall *pfnsetpal  )(HMIBMP* bmp,LPVOID palette);
extern "C" typedef void    (__stdcall *pfndrawrect)(HMIBMP* bmp,int x,int y,int w,int h,int color);
extern "C" typedef HMIBMP* (__stdcall *pfnsizebmp )(HMIBMP* bmp,int w,int h);

//PUDgrp.inc
extern "C" typedef LPVOID  (__stdcall *pfngrpinit )(LPVOID grplubase);
extern "C" typedef void    (__stdcall *pfndrawgrp )(int grp,int nframe,LPVOID daddr,int dwidth,int owner,int centre,int mirror);
extern "C" typedef void    (__stdcall *pfndrawbtn )(LPVOID bmp, int xpos, int ypos, int icon, int state);




extern rfngetname      RESgetName;
extern rfntileset      RESgetTileset;
extern rfngetpal       RESgetPalette;
extern rfngetgrp       RESgetGrp;

extern pfnpudload      PUDload;
extern pfnpudsave      PUDsave;

extern pfnsectcnt      PUDsection_count;
extern pfndestroy      PUDdestroy;
extern pfndelsect      PUDdelete_section;
extern pfnfindsect     PUDfind_section;
extern pfnupdsect      PUDupdate_section;
extern pfngetsect      PUDget_section;
extern pfnsectndx      PUDsection_index;
extern pfnsectset      PUDsection_set_index;
extern pfnsectcre      PUDcreate_section;
extern pfnrealloc      PUDsection_realloc;

extern pfnunitsize     PUDunitSize;
extern pfnuniticof     PUDunitIconFrame;
extern pfnlistget      PUDlistGet;
extern pfnlistnext     PUDlistNext;
extern pfnlistprev     PUDlistPrev;
extern pfnlistname     PUDlistName;

extern pfnrndrmap      PUDrenderMap;
extern pfnrndrtile     PUDrenderTile;
extern pfngettile      PUDgetTile;
extern pfntileset      PUDtileset;
extern pfnblack        PUDblack;

extern pfnmakebmp      make_bitmap;
extern pfnfreebmp      free_bitmap;
extern pfnzerobmp      zero_bitmap;
extern pfnsavebmp      save_bitmap;
extern pfnsavejpg      save_jpeg;
extern pfnperfbmp      perforate_bitmap;
extern pfncopyimg      copy_image;
extern pfncopyfull     copy_full_image;
extern pfndispimg      display_image;
extern pfnsetpal       set_palette;
extern pfndrawrect     draw_rect;
extern pfnsizebmp      size_bitmap;

extern pfngrpinit      GRPinit;
extern pfndrawgrp      GRPdraw;
extern pfndrawbtn      GRPdrawButton;


#endif


PUDPIC.cpp
Code: [Select]
#include <windows.h>
#include <lamb.h>


HMODULE hResDll  = LoadLibraryA("LambRES");
HMODULE hLambDll = LoadLibraryA("LambWC2");


rfngetpal   RESgetPalette       = (rfngetpal  )GetProcAddress(hResDll , "RESgetPalette"       );
rfngetgrp   RESgetGrp           = (rfngetgrp  )GetProcAddress(hResDll , "RESgetGrp"           );
rfngetname  RESgetName          = (rfngetname )GetProcAddress(hResDll , "RESgetName"          );
rfntileset  RESgetTileset       = (rfntileset )GetProcAddress(hResDll , "RESgetTileset"       );


pfnpudload  PUDload             = (pfnpudload )GetProcAddress(hLambDll, "PUDload"             );
pfnpudsave  PUDsave             = (pfnpudsave )GetProcAddress(hLambDll, "PUDsave"             );

pfnsectcnt  PUDsection_count    = (pfnsectcnt )GetProcAddress(hLambDll, "PUDsection_count"    );
pfndestroy  PUDdestroy          = (pfndestroy )GetProcAddress(hLambDll, "PUDdestroy"          );
pfndelsect  PUDdelete_section   = (pfndelsect )GetProcAddress(hLambDll, "PUDdelete_section"   );
pfnfindsect PUDfind_section     = (pfnfindsect)GetProcAddress(hLambDll, "PUDfind_section"     );
pfnupdsect  PUDupdate_section   = (pfnupdsect )GetProcAddress(hLambDll, "PUDupdate_section"   );
pfngetsect  PUDget_section      = (pfngetsect )GetProcAddress(hLambDll, "PUDget_section"      );
pfnsectndx  PUDsection_index    = (pfnsectndx )GetProcAddress(hLambDll, "PUDsection_index"    );
pfnsectset  PUDsection_set_index= (pfnsectset )GetProcAddress(hLambDll, "PUDsection_set_index");
pfnsectcre  PUDcreate_section   = (pfnsectcre )GetProcAddress(hLambDll, "PUDcreate_section"   );
pfnrealloc  PUDsection_realloc  = (pfnrealloc )GetProcAddress(hLambDll, "PUDsection_realloc"   );


pfnunitsize PUDunitSize         = (pfnunitsize)GetProcAddress(hLambDll, "PUDunitSize"         );
pfnuniticof PUDunitIconFrame    = (pfnuniticof)GetProcAddress(hLambDll, "PUDunitIconFrame"    );
pfnlistget  PUDlistGet          = (pfnlistget )GetProcAddress(hLambDll, "PUDlistGet"          );
pfnlistnext PUDlistNext         = (pfnlistnext)GetProcAddress(hLambDll, "PUDlistNext"         );
pfnlistprev PUDlistPrev         = (pfnlistprev)GetProcAddress(hLambDll, "PUDlistPrev"         );
pfnlistname PUDlistName         = (pfnlistname)GetProcAddress(hLambDll, "PUDlistName"         );

pfnrndrmap  PUDrenderMap        = (pfnrndrmap )GetProcAddress(hLambDll, "PUDrenderMap"        );
pfnrndrtile PUDrenderTile       = (pfnrndrtile)GetProcAddress(hLambDll, "PUDrenderTile"       );
pfngettile  PUDgetTile          = (pfngettile )GetProcAddress(hLambDll, "PUDgetTile"          );
pfntileset  PUDtileset          = (pfntileset )GetProcAddress(hLambDll, "PUDtileset"          );
pfnblack    PUDblack            = (pfnblack   )GetProcAddress(hLambDll, "PUDblack"            );

pfnmakebmp  make_bitmap         = (pfnmakebmp )GetProcAddress(hLambDll, "make_bitmap"         );
pfnfreebmp  free_bitmap         = (pfnfreebmp )GetProcAddress(hLambDll, "free_bitmap"         );
pfnzerobmp  zero_bitmap         = (pfnzerobmp )GetProcAddress(hLambDll, "zero_bitmap"         );
pfnsavebmp  save_bitmap         = (pfnsavebmp )GetProcAddress(hLambDll, "save_bitmap"         );
pfnsavejpg  save_jpeg           = (pfnsavejpg )GetProcAddress(hLambDll, "save_jpeg"           );
pfnsizebmp  size_bitmap         = (pfnsizebmp )GetProcAddress(hLambDll, "size_bitmap"         );
pfnperfbmp  perforate_bitmap    = (pfnperfbmp )GetProcAddress(hLambDll, "perforate_bitmap"    );
pfncopyimg  copy_image          = (pfncopyimg )GetProcAddress(hLambDll, "copy_image"          );
pfncopyfull copy_full_image     = (pfncopyfull)GetProcAddress(hLambDll, "copy_full_image"     );
pfndispimg  display_image       = (pfndispimg )GetProcAddress(hLambDll, "display_td_image"    );
pfnsetpal   set_palette         = (pfnsetpal  )GetProcAddress(hLambDll, "set_palette"         );
pfndrawrect draw_rect           = (pfndrawrect)GetProcAddress(hLambDll, "draw_rect"           );

pfngrpinit  GRPinit             = (pfngrpinit )GetProcAddress(hLambDll, "GRPinit"             );
pfndrawgrp  GRPdraw             = (pfndrawgrp )GetProcAddress(hLambDll, "GRPdraw"             );
pfndrawbtn  GRPdrawButton       = (pfndrawbtn )GetProcAddress(hLambDll, "GRPdrawButton"       );


CHAR* pudpath = "this.pud";
CHAR* bmppath = "this.bmp";
CHAR* jpgpath = "this.jpg";

BYTE* sectionData(LPVOID hpud, DWORD sname){
    PUDSECT* sptr = PUDfind_section(hpud,sname);
    if (sptr!=NULL) return (BYTE*)sptr->data;
    return NULL;
}

int sectionSize(LPVOID hpud, DWORD sname){
    PUDSECT* sptr = PUDfind_section(hpud,sname);
    if (sptr!=NULL) return sptr->size;
    return 0;
}

int getPudSize(LPVOID hpud){
    WORD* pdim = (WORD*) sectionData(hpud,sDIM_);
    if(pdim) return (int) *pdim;
    return 0;
}

int getTerrainType(LPVOID hpud){
    WORD* pter = (WORD*) sectionData(hpud,sERA_);
    if(pter) return (int) *pter;
    return 0;
}

BOOL isBuilding(int utype){
    // is the unit a building?
    return utype>=0x3A;
}

void renderUnit(HMIBMP* hbm, PUDUNIT* unit){
    // draw a unit grp
   
    int   frame = 0;
    int   x=(unit->x+1)*TILEW;
    int   y=(unit->y+1)*TILEW;
    int   centre = 0;
    int   rev = 0;
   
   
    if(!isBuilding(unit->type)){
       // units need to be centred and offset
       centre = 1;
       x+=TILEW2;
       y+=TILEW2;
       
       // pick a random direction so they dont all face the same way
       frame = rand()%5;
       rev   = rand()%2;    // draw reversed?
    }
   
    GRPdraw( unit->type, frame, hbm->pBits+x+(y*hbm->linew), hbm->linew, unit->owner, centre, rev );

}


///////// START /////////


    // load the pud file
    LPVOID hPud = PUDload(pudpath);
   
   
    // get some info from the pud
    int mapsize = getPudSize(hPud);
    int terrain = getTerrainType(hPud);
   

    // make a bitmap in memory (tiles are 32x32 pixels)
    HMIBMP* hbm = make_bitmap(mapsize*TILEW,mapsize*TILEW,8);
   
   
   
    /// TERRAIN ///
   
    // use the tiles for the map terrain type
    PUDtileset(RESgetTileset(terrain));
   
    // don't forget this if you are using GRPs, only needs to be done once to initialize
    GRPinit(RESgetGrp());
   
    //paste all the tile graphics for the pud onto the bitmap
    PUDrenderMap( hbm, sectionData(hPud,sMTXM) , mapsize);
   
       
    /// UNITS ///
           
    // get a pointer to the "UNIT" section
    PUDSECT* units = PUDfind_section(hPud,sUNIT);
       
    if(units){
         
        // make a slightly larger bitmap to paste units on
        // --> some units like flyers can be placed in locations where they
        //     will render off the edge of the map (crash and burn).
        //
        // so here we make a bitmap with a safety margin around the edge
       
        HMIBMP* hUbm = make_bitmap((mapsize+2)*TILEW,(mapsize+2)*TILEW,8);
             
        // how many units on the pud?
        // ( includes mines, start locations etc.)
        int nUnits  = units->size/sizeof(PUDUNIT);
       
        // draw the unit GRPs on the 'safe' bitmap
        PUDUNIT* pUnit = (PUDUNIT*)units->data;
        for(int i=0;i<nUnits;i++){
            renderUnit(hUbm,pUnit);
            pUnit++;
        }
       
        // copy the unit bitmap (less the border area) onto the terrain bitmap
        copy_image(hUbm,TILEW,TILEW,hbm->width,hbm->height,hbm,0,0,0); // <- last 0 is the transparent index
                                                                       //    pass -1 for none.
        // dispose of the unit bitmap
        free_bitmap(hUbm);
    }
   
   
    // ensure the bmp file has the correct palette for the terrain type
    set_palette(hbm , RESgetPalette( terrain ));
   
    // save the full bitmap to a .bmp file
    save_bitmap(bmppath,hbm);
   
    // resize the bitmap then save to a .jpg file
    HMIBMP* hJbm = size_bitmap(hbm,1024,1024);
    save_jpeg(jpgpath,hJbm,90); // <-- quality 90 looks nice
   
    // dispose of bitmaps
    free_bitmap(hbm);
    free_bitmap(hJbm);
   
    // dispose of the pud structure
    PUDdestroy(hPud);


                                                            :critter:

its gooder to hax hard and NEVER get caught!

Offline Cel

  • Axe Thrower
  • ****
  • Posts: 432
    • View Profile
Re: Nerd's Corner
« Reply #155 on: October 14, 2018, 12:40:20 AM »
Nice thanks! ^-^ :thumbsup:

Interesting that they save the transition type value from the marching squares algorithm in the pud directly instead of calculating it once at load time. It means your map editor has to save these calculated values in the pud but it also means you can save a pud files with very weird transitions if you would like to that could look funny. (:

 :critter:
« Last Edit: October 14, 2018, 12:53:13 AM by Cel »

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #156 on: October 14, 2018, 07:42:34 AM »
Nice thanks! ^-^ :thumbsup:

Interesting that they save the transition type value from the marching squares algorithm in the pud directly instead of calculating it once at load time. It means your map editor has to save these calculated values in the pud but it also means you can save a pud files with very weird transitions if you would like to that could look funny. (:

 :critter:



yw :)

The terrain transition tile values can be derived like THIS.

For displaying terrain in the PUDPIC program I cheated - big time ;)

I generated a list of possible MTXM values based on my assumptions of the format, then also searched every pud in my maps directory for any crazy custom values I had missed and added them too.

Then I auto generated maps with arrays of these MTXM values, then loaded the maps, took SS, and then processed the SS to pull out the exact 32x32 block of pixels for each map value.


 ... stuff like this:






Finally I searched all the pixel blocks and weeded out any repeats, then turned them into an array of pixel data indexed by the MTXM value list - with multiple indexes to the same pixels for different tile values that displayed them.

In this way when rendering there is no need to calculate and locate the 4 different 8x8 mini-tiles that make up each MTXM location, it just uses the raw MTXM value to do a lookup in a pointertable  which supplies the exact location of the full 32x32 block.

                                                                                :critter:


 -- edit --

The units are all rendered using the original grp files, using the method detailed HERE


« Last Edit: October 15, 2018, 04:36:51 AM by Lambchops »
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #157 on: October 22, 2018, 11:43:31 AM »
u are invited to my xmas party

Thanks @easycompany - as soon as my fairy godmother buys me a plane ticket and pays for me to get a passport, I'll be there with bells on ...

  ... so probably never lol, but it's a nice thought - I'd be happy to help you drink some beers. Maybe one day.
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #158 on: January 23, 2019, 11:28:04 PM »
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #159 on: February 27, 2019, 09:52:21 PM »
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #160 on: March 01, 2019, 06:52:19 AM »
Source code for War2FontTool.exe (description)
Code: [Select]
// Warcraft II font Extractor/Compiler
// Lambchops Feb 2019

#include <windows.h>
#include <shlwapi.h>

#define INVALID_FILE  1
#define INVALID_INPUT 2
#define READ_ERROR    3
#define WRITE_ERROR   4
#define ALLOC_FAIL    5

#define BG_COLOR 0x18
#define MAX_CHAR_SIZE 4108


typedef struct   _FontHeader {
   DWORD  FourCC;              //   Always "FONT"
   BYTE   LowIndex;            //   Index of first letter in file
   BYTE   HighIndex;           //   Index of the last letter in file
   BYTE   MaxWidth;            //   Maximum width
   BYTE   MaxHeight;           //   Maximum height
} FontHeader;

typedef struct  _FontLetterRaw {
   BYTE   Width;     //   Width of the letter
   BYTE   Height;    //   Height of the letter
   BYTE   XOffset;   //   X Offset for the topleft corner of the letter.
   BYTE   YOffset;   //   Y Offset for the topleft corner of the letter.
} FontLetterRaw;

BYTE clrndx[ ] = {BG_COLOR,0xC8,0xC7,0x4A,0xC0,0x00};

char f_ext[] = "fnt";
char b_ext[] = "bmp";
char szBmpTmpl[] = "_%3.3d.bmp";
char szFntTmpl[] = ".fnt";
char szFont4cc[] = "FONT";
char pathbuf[512];
char szHelpText[]   = "  Warcraft II Font Extractor/Compiler - Lambchops Feb 2019\r\rDrop a .fnt file to extract.\rDrop one of the extracted .bmp files to compile.\r\r *Make sure you only use the existing colors when editing\r  unknown colors will be deleted.";


// --- FILE/PATH --- //

int get_flen(CHAR* path){
    HANDLE fn = CreateFile(path,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
    int r = GetFileSize(fn,NULL);
    CloseHandle(fn);
    return r;
}

CHAR* filename(CHAR* lppath){
CHAR* r = lppath+lstrlen(lppath);
while(r>lppath){
if(*r==0x5C)return (r+1);
r--;
}
return lppath;
}

CHAR* get_extension(CHAR* lppath){
CHAR* r = lppath+lstrlen(lppath);
CHAR* def = r;
while(r>lppath){
if(*r==0x2E)return (r+1);
r--;
}
return def;
}

int loadlen;

HANDLE bload(char* path){
    // Allocate global memory and read in a file
    HANDLE fn = CreateFile(path,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
    DWORD nb;
    HANDLE fdat;

    if(fn!=INVALID_HANDLE_VALUE){
        loadlen = GetFileSize(fn,NULL);

        // allocate a buffer
        fdat = GlobalAlloc(GPTR,loadlen);

        // read file into buffer
        ReadFile(fn,fdat, loadlen, &nb, NULL);
        CloseHandle(fn);

       if(nb==loadlen)return fdat;             
       GlobalFree(fdat);
    }
    return NULL;
}



// --- BITMAP --- //

int bmp8len = 0x1436;
DWORD bmp8[] = {0x14364D42,0,0x04360000,0x00280000,0x00400000,0x00400000,0x00010000,0x00000008,0x10000000,0,0,0,0,0,0x94FC0000,
0xD0000000,0xFF0000FC,0x00FC0000,0x00FC0000,0x00FC0000,0x00FC0000,0x00FC0000,0x00FC0000,0x28280000,0x00FC00CC,0xFC480000,0xCC2800FC,0xA01000E4,0x740000CC,0x240000B4,0x2C000000,
0x34000000,0x40040014,0x4804000C,0x540C0024,0x5C100028,0x6414002C,0x6C1C003C,0x7828004C,0x8030005C,0x8C3C006C,0x4C000080,0x20540000,0x285C0000,0x2C640000,0x30680000,0x34700000,
0x38740004,0x44780004,0x50800008,0x5C880010,0x487C0018,0x4C80000C,0x4880000C,0x447C000C,0x40780008,0x38740008,0x38740004,0x38740004,0x38740004,0x40780004,0x50840008,0x5C8C0010,
0x6C940018,0x608C0024,0x5888001C,0x50840014,0x20040010,0x2808003C,0x34100048,0x042C0058,0x042C00FC,0x540000FC,0x60040000,0x70340010,0x7C1C003C,0xA038004C,0x04040080,0x10100004,
0x18180010,0x24240018,0x30300024,0x3C3C0030,0x4848003C,0x50500048,0x5C5C0050,0x6868005C,0x74740068,0x7C7C0074,0x8888007C,0x94940088,0xA0A00094,0xACAC00A0,0x440000AC,0x14000000,
0x1C000018,0x20000024,0x2C000030,0x30000040,0x38000050,0x40000060,0x4404006C,0x4C000074,0x54080078,0x6018007C,0x6C280088,0x7C3C0090,0x844C0098,0x3C0000A0,0x14140000,0x1C1C0014,
0x2828001C,0x34340028,0x40400034,0x4C4C0040,0x5858004C,0x64640058,0x6C6C0064,0x7878006C,0x84840078,0x90900084,0x9C9C0090,0xA8A8009C,0xB4B400A8,0xC0C000B4,0xCCCC00C0,0xD8D800CC,
0x343400D8,0x44400044,0x54500054,0x64600068,0x78700078,0x8880008C,0x9C90009C,0xB0A400B0,0xC4B400C0,0x485800D4,0x5C6C0030,0x74800044,0x8C94005C,0x9CA4007C,0x14000090,0x18000024,
0x2000002C,0x24040034,0x2C04003C,0x30080048,0x380C0050,0x40140058,0x4C180064,0x581C006C,0x60240078,0x6C280080,0x7830008C,0x84380094,0x944000A0,0xA04800A8,0x340000B4,0x40000018,
0x4C00001C,0x58000024,0x64040028,0x70040030,0x80080034,0x8810003C,0x9414004C,0xA018005C,0xAC20006C,0xB828007C,0x34140090,0x48140074,0x6418009C,0x841400C4,0x000000F0,0x00000018,
0x0000002C,0x00000044,0x0000005C,0x00000074,0x00000088,0x000000A0,0x000000B8,0x381000D0,0x4C1C0060,0x64280074,0x7C3C0088,0x9054009C,0xA87400B4,0xC49800CC,0x002400E8,0x00300000,
0x04400000,0x0C4C0000,0x185C0000,0x28700000,0x34780000,0x40840004,0x5090000C,0x60980018,0x70A40024,0x80B00034,0xF4580044,0xB43800FC,0x601800FC,0x000000FC,0x300000FC,0x4000005C,
0x50040068,0x64080078,0x780C0088,0x8C100098,0xA41400A8,0xC01C00BC,0xE02000D0,0x000000F4,0x000000FC,0x94FC00B0,0xD0000000,0x000000FC,0x00FC00FC,0xFC000000,0,0x000000A4,
0x0400007C,0x0400005C,0x48CC0044,0x28A0000C,0x14740004,0x044C0000,0xB4940000,0x845C002C,0x542C0014,0x280C0004,0x48B00000,0x2C840098,0x18580074,0x082C0050,0x8C14002C,0x601000F8,
0x3C1000C8,0x200C0098,0x283C006C,0x1C2C0028,0x1420001C,0x0C140014,0xE0E0000C,0x98B400E0,0x54800098,0x284C0054,0x40140024,0x58200068,0x7030007C,0x00000090,0x5CA00000,0x34780018,
0x50840004,0x6C8C0010,0x80E40018,0x8448001C,0xF8F000A8,0xA0A400FC,0x808000A0,0x00000080,0xFC0000FC,0xFC000000,0x00FC00FC,0x00FC0000,0xFCFC00FC,0xFCFC0000,0x000000FC,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

BYTE* bmptr = (BYTE*)bmp8;
BYTE* pixmap;
int linew,bmw,bmh;

void init_bmp(int w,int h){
    // initialize the bitmap buffer for the supplied size
   
    BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)bmptr;
    BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)(bmptr+sizeof(BITMAPFILEHEADER));
                   pixmap = bmptr+bfh->bfOffBits;
                    linew = ((w+3)>>2)<<2;
    int             psize = linew*h;
   
    bfh->bfSize  = bfh->bfOffBits+psize;
    bih->biWidth = w;
    bih->biHeight= h;
    bmw = bih->biWidth;
    bmh = bih->biHeight;
    FillMemory(pixmap,psize,BG_COLOR);
}



void set_pixel(int x,int y,int c){
    // set a pixel in the current bitmap
    BYTE* pptr = pixmap+(linew*(bmh-y-1))+x;
    *pptr = c;
}

int get_pixel(int x,int y){
    // get a pixel from the current bitmap
    BYTE* pptr = pixmap+(linew*(bmh-y-1))+x;
    return *pptr;
}

void normalize_bmp(){
    // change palette indexes to font indexes     
    //  change unknown values to transparent

    int c;
    for(int y=0;y<bmh;y++){
        for(int x=0;x<bmw;x++){
            c = get_pixel(x,y);
            set_pixel(x,y,0);
            for(int i=0;i<=5;i++){
                if(c==clrndx[i]){
                    set_pixel(x,y,i);
                    break;
                }
            }
        }
    }
}

int bmp_yos(){
    // find non-zero Y offset
    for(int y=0;y<bmh;y++){
        for(int x=0;x<bmw;x++){
            if(get_pixel(x,y)!=0)return y;
        }
    }
    return -1;
}

int bmp_xos(){
    // find non-zero X offset
    for(int x=0;x<bmw;x++){   
        for(int y=0;y<bmh;y++){
            if(get_pixel(x,y)!=0)return x;
        }
    }
    return -1;
}

BOOL save_bmp(char* path){
    // save the current bitmap
    BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)bmptr;   
    HANDLE fn = CreateFile(path,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
    DWORD nb;
   
    if(fn!=INVALID_HANDLE_VALUE){
        if(WriteFile(fn,bmptr,bfh->bfSize,&nb,NULL)){
            CloseHandle(fn);
            return TRUE;
        }
        CloseHandle(fn);
    }
    return FALSE;
}



BOOL load_bmp(char* path){
    BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)bmptr;
    BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)(bmptr+sizeof(BITMAPFILEHEADER));
    HANDLE fn = CreateFile(path,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
    DWORD nb;

    if(fn!=INVALID_HANDLE_VALUE){
        loadlen = GetFileSize(fn,NULL);

        // read file into buffer
        ReadFile(fn,bmp8, loadlen, &nb, NULL);
        CloseHandle(fn);

       if(nb==loadlen){
           bmw    = bih->biWidth;
           bmh    = bih->biHeight;
           pixmap = bmptr+bfh->bfOffBits;
           linew  = ((bmw+3)>>2)<<2;
             
           return TRUE;
       }
    }
    return FALSE;   
}

BITMAPINFOHEADER* bmp_info(){
    return (BITMAPINFOHEADER*)(bmptr+sizeof(BITMAPFILEHEADER));               
}

// --- EXTRACT --- //


BOOL extract_char(HANDLE hFont,int base,int c,char* path){
    FontLetterRaw* flr;
    BYTE*  bptr = (BYTE*)hFont;
    DWORD* dptr = (DWORD*)hFont;
    int    px = 0;
    int    py = 0;

    // get file offset   
    dptr += (c-base+2);
    if(!(*dptr))return FALSE;
    bptr +=(*dptr);
   
    // read character header
    flr   =(FontLetterRaw*)bptr;
    bptr += sizeof(FontLetterRaw);
   
    // initialize output bitmap
    init_bmp( flr->Width+flr->XOffset, flr->Height+flr->YOffset );
   
    // set pixels
    while(py<flr->Height){
               
        // read data byte
        c = *bptr;       
        bptr++;
       
        // apply shift
        px+=(c>>3);
        c&=7;
       
        // wrap
        while(px>=flr->Width){
            px -= flr->Width;
            py++;
        }
       
        // set pixel
        if(c&&py<flr->Height){
            set_pixel( px+flr->XOffset, py+flr->YOffset, clrndx[c] );   
            px++;
        }
    }
    save_bmp(path);
}

BOOL test4cc(char* a,char* b){
    for(int i=0;i<4;i++){
        if(*a!=*b)return FALSE;       
        a++;
        b++;
    }
    return TRUE;
}

int do_extract(char* path){
    FontHeader* fh;
    char* bpath;
    HANDLE hFont = bload(path);
       
    if(hFont!=NULL){
        // verify 4CC
        if(test4cc((char*)szFont4cc,(char*)hFont)){
           
            // prepare output path
            lstrcpy(pathbuf,path);
            bpath = get_extension(pathbuf)-1;
                                                   

            // read the file header
            fh = (FontHeader*)hFont;
           
            // process the characters
            for(int c=fh->LowIndex;c<=fh->HighIndex;c++){
                // make bmp filename
                wsprintf(bpath,szBmpTmpl,c);
                // extract the character
                extract_char(hFont,fh->LowIndex,c,pathbuf);
            }
           
            GlobalFree(hFont);   
            return EXIT_SUCCESS;

        }
        GlobalFree(hFont);
        return INVALID_FILE;

    }
    return READ_ERROR;
}


// --- COMPILE --- //

int compile_char(BYTE* optr){
    // write raw data for current bmp char at optr
    // return data size in bytes
   
    int c,t;
    FontLetterRaw* flr = (FontLetterRaw*)optr;
    optr+=sizeof(FontLetterRaw);   
   
    // set character header
    int yos = bmp_yos();
    if(yos==-1)return 0;
    flr->YOffset = yos;
    flr->XOffset = bmp_xos();
    flr->Width   = bmw-flr->XOffset;
    flr->Height  = bmh-flr->YOffset;
   
    // compile pixels
    t=0;
    for(int y=flr->YOffset;y<bmh;y++){
        for(int x=flr->XOffset;x<bmw;x++){
            c=get_pixel(x,y);
            if(c){
                *optr=(t<<3)+c;
                t=0;
                optr++;
            }else{
                t++;
                if(t==31){
                    *optr=0xF8;
                    optr++;
                    t=0;
                }
            }
        }
    }
    if(t){
        *optr=(t<<3);
        optr++;             
    }
   
    return (int)optr-(int)flr;
}


int do_compile(CHAR* path){
    HANDLE fn;
    DWORD nb;
    int len;
    BITMAPINFOHEADER* bih;
    FontHeader* fh;
    BYTE* hFont;
    char* bpath;
    DWORD* fptr;
    BYTE* optr;
    int datsize;
    int maxw = 0;
    int maxh = 0;
    int minc = 0x20;
    int maxc = 0;
    int nchar= 0;
   
    // enum char bitmaps
    lstrcpy(pathbuf,path);
    bpath = get_extension(pathbuf)-5;

    for(int i=1;i<256;i++){
        wsprintf(bpath,szBmpTmpl,i);
        if(PathFileExists(pathbuf)){       
            if(get_flen(pathbuf)<=bmp8len){
                if(i<minc)minc=i;
                if(i>maxc)maxc=i;
                load_bmp(pathbuf);
                bih = bmp_info();
                if(bih->biWidth>maxw)maxw=bih->biWidth;
                if(bih->biHeight>maxh)maxh=bih->biHeight;
            }                   
        }
    }
       
    if(!maxw*maxh*maxc)return INVALID_INPUT;
    if(maxc<minc){ minc=maxc; maxc=0x20; }
    nchar = maxc-minc+1;
   
    // allocate output buffer
    hFont = (BYTE*)GlobalAlloc(GPTR,nchar*MAX_CHAR_SIZE);
    if(!hFont)return ALLOC_FAIL;
   
    // set up pointers
    fh=(FontHeader*)hFont;
    fptr=(DWORD*)(hFont+sizeof(FontHeader));
    optr=(BYTE*)(hFont+sizeof(FontHeader)+nchar*4);
   
    // write font header
    lstrcpy((char*)hFont,szFont4cc);
    fh->LowIndex  = minc;
    fh->HighIndex = maxc;
    fh->MaxWidth  = maxw;
    fh->MaxHeight = maxh;
   
    // compile characters
    for(int c=minc;c<=maxc;c++){
        datsize = 0;
        wsprintf(bpath,szBmpTmpl,c);
        if(PathFileExists(pathbuf)){
            len = get_flen(pathbuf);
            if(len&&len<=bmp8len){
                if(!load_bmp(pathbuf)){
                    GlobalFree(hFont);
                    return READ_ERROR;
                }
                normalize_bmp();
                datsize = compile_char(optr);
            }
        }
        // write file offset
        if(datsize)*fptr=optr-hFont;
        fptr++;
        optr+=datsize;
    }
   
    // write fnt file
   
    datsize=optr-hFont;
    lstrcpy(bpath,szFntTmpl);
    fn = CreateFile(pathbuf,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
   
    if(fn==INVALID_HANDLE_VALUE){
        GlobalFree(hFont);
        return WRITE_ERROR;
    }else{
        if(WriteFile(fn,hFont,datsize,&nb,NULL)){
            CloseHandle(fn);
            GlobalFree(hFont);
            if(nb==datsize)return 0;
            return WRITE_ERROR;
        }
        CloseHandle(fn);
        GlobalFree(hFont);
        return WRITE_ERROR;
    }
}


// --- HELP --- //

int do_help(){
    MessageBox(0,szHelpText,"War2 Font Tool",0);
    return EXIT_SUCCESS;
}
// --- MAIN --- //


int main(int argc, char *argv[]){
    CHAR* e;
    if(lstrlen(argv[1])>3){
        e=get_extension(argv[1]);
        if(lstrlen(e)!=3)return INVALID_FILE;
        if(!lstrcmpi(e,f_ext))return do_extract(argv[1]);
        if(!lstrcmpi(e,b_ext))return do_compile(argv[1]);
    }else{
        return do_help();
    }
}
« Last Edit: March 07, 2019, 09:03:49 PM by Lambchops »
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #161 on: March 29, 2019, 06:55:54 AM »
                 HOSTING INTERFACE



NOTE: This project has been repurposed as the WC2 Plugin Framework (BELOW)




The hosting / port forwarding / firewall hole punching thing has been an issue since BNE came out.

I started on a fix for this last year, however my current work commitments mean that I am unlikely to find the time to finish this in the forseeable future.

I have completed what is probably the trickiest part of the project which is to provide an interface between the appropriate parts of the wc2 process and an external program, so I have decided to release this in the hope that someone else can put the time into developing and testing the rest of it.



---------------------------------------------------


So how does this work?

If you copy the included PE files into an existing BNE installation you should find that when you join a game you will get a message on the screen saying "JOINING" then the game name, and likewise when you create a game you will get a message saying "HOSTING" then the game name.

If you look at the host.dll source code you will see where these messages are being generated.



The host.dll module is required to export 3 functions called "w2p_init", "create_game" and "join_game".


>  w2p_init has no arguments and is called when the exe is first started. Any initialization code should go here.

>  create_game will be passed a pointer to the game name and called when the player creates a game.

>  join_game will be passed a pointer to the game name and called when the player joins a game.


----------------------------------------


So what needs to be done with this to fix the hosting problem?


There's a bit of work to do yet, but its all bread & butter networking stuff so I'm hoping there are people in the community that can do the job....

You will need to make a custom server, and your own "host.dll" that exports those 3 functions. The example source is in C++, however any language capable of building a normal windows dll can be used.

w2p_init() will just have whatever initialization code is required to support the rest of the project. Possibly a TCP connection to the hosting server could be started here, but personally I think this would be more reliable being done on a game-by-game basis.

join_game() should be fairly simple, it just needs to send a message (probably over TCP) to the server saying "im trying to join this game" with the player's name, the game name, and the client's game port, the terminate the connection. Probably the server should be able to find the ip address from the incoming message, or possibly the client will have to obtain its own external address and supply it as part of the message.

create_game() when hosting, the client will need to negotiate a TCP connection with the server and supply the name of the game it is hosting, then start a thread that listens to that connection for messages from the server. It should get a message from the server over the TCP connection when a player is trying to join with the IP address and game port of the joining client. When this message is received it will need to ping the joining cllient with appropriate UDP packets to facillitate the hole-punching. Finally it will need to notify the server when the game is starting then terminate the listening thread.

The Server:

The server will need to listen for and accept incoming TCP connection from clients, and maintain a list of the active hosting connections.

When a client notifies that it is hosting a game, the game name should be stored in the list.

When a hosting client notifies that the game has started, terminate the connection and remove the game from the list.

When a client notifies that it is joining a game, look up the game name in the list, then notify the hosting client of the joining clients IP address and game port.

Joining clients are not required to be stored in the list for basic functionality, however it might be a god idea to store the info for a short amount of time to protect against faulty clients or DDOS etc.


    --------------------------


There will no doubt be lots of lots of dev and testing required, but I think it is quite achievable. I am very happy to offer whatever technical support I can, but I simply don't have the time right now to develop the whole thing on my own.


one, two, three ..... GO!   

                                                     :critter:


« Last Edit: June 05, 2019, 10:28:01 PM by Lambchops »
its gooder to hax hard and NEVER get caught!

Offline fois

  • Grunt
  • ***
  • Posts: 230
  • Chicken
    • View Profile
Re: Nerd's Corner
« Reply #162 on: March 29, 2019, 04:19:07 PM »
Interesting... I had a whole different idea, my plan was to create a wsock32 proxy that works with any version of the game (without any kind of game patches).

I wanted to catch the packet that's being sent when you try to join a room, take it and append a id (a crc32 of the host's name possibly) and forward it to a server. The server will now forward the packet to the host (It knows where to send it, it reads the id) and appends the IP/Port of the sender. The host will now receive the packet from the server containing the original senders IP/Port, he will now send a packet to the original sender and that's pretty much it, holepunching is done :)

For this to work I would've created another thread that keeps sending a keep alive packet each 30seconds or so to the server, this is where the players announce their ids. The server will save the id/ip+port combo and store it, this way it always knows where to forward the packets when someone tries to join a game.

This is supposed to be a fully UDP solution, no changes or any kind of interaction with the pvpgn server are required.
« Last Edit: March 29, 2019, 04:37:30 PM by fois »

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #163 on: March 30, 2019, 07:26:36 AM »
Interesting... I had a whole different idea, my plan was to create a wsock32 proxy that works with any version of the game (without any kind of game patches).

I wanted to catch the packet that's being sent when you try to join a room, take it and append a id (a crc32 of the host's name possibly) and forward it to a server. The server will now forward the packet to the host (It knows where to send it, it reads the id) and appends the IP/Port of the sender. The host will now receive the packet from the server containing the original senders IP/Port, he will now send a packet to the original sender and that's pretty much it, holepunching is done :)

For this to work I would've created another thread that keeps sending a keep alive packet each 30seconds or so to the server, this is where the players announce their ids. The server will save the id/ip+port combo and store it, this way it always knows where to forward the packets when someone tries to join a game.

This is supposed to be a fully UDP solution, no changes or any kind of interaction with the pvpgn server are required.

Ok, so pretty much the same thing except you are hooking send and recv wsock32.dll instead of create and join storm.dll and bne.exe, and using player names instead of game names.

Requires you to be able to identify "join" packets, then persumably store and resend them after the host has responded, or does the game do some retrys? This is why I hooked the join function so the hole punching is done before the join packet is sent.

As to it being "fully UDP" regardless you still need an outgoing connection to the server, I don't know why you would want to keep pinging the server with UDP when this is what TCP does automatically, why not just use TCP and not worry about it?

Personally I think hooking create and join is a better idea than messing with/adding latency to all the in-game packets as well. I've never messed with packet spying/modding/injecting for the game, although I understand there were some oldschool hacks that worked that way.

Are the "join" packets the same for every version of the game?

Ultimately it seems a bit more complicated than using the framework I have already provided, but if you can make it work then great! It would be very neat to just have a single dll that can be dropped into an existing installation. :)

-- edit --

BTW if anyone successfully uses my interface as part of a solution you can keep all of tk's $500, it's provided free in the hope that someone has time to do the work.
« Last Edit: March 30, 2019, 07:50:15 AM by Lambchops »
its gooder to hax hard and NEVER get caught!

Offline Lambchops

  • Ogre Mage
  • ********
  • Posts: 1541
    • View Profile
Re: Nerd's Corner
« Reply #164 on: May 02, 2019, 08:33:37 AM »
              Warcraft II Plugin Framework

                                              ( including demo plugins )
 


To install download the zip file attached to this post and copy/replace its contents into a combat43 or Warcraft II BNE 2.02 installation.
*Should also work for any versions earlier than 4.3 - AFAIK they all use the same wc2 .exe file.

Currently tested and working with Windows 10, 7 and XP.


Note: The plugin framework currently only supports BNE 2.02 (Combat Edition 4.3 or earlier), it does not support CE 4.4 / GOG version. If there is a demand it can be ported at a later date.


  ----------------------


This will install a small file called "LC.dll" and make a few modifications to Warcraft II BNE.exe that link it.

A "plugins" folder will be created in the wc2 folder. Plugins can be placed in this folder will be automatically loaded when you run wc2.

This will allow a variety of mods to be easilly installed/uninstalled by users.

If there is nothing in the "plugins" folder, there is no change to how the game functions.

  -------------

For developers:
Spoiler
A plugin is simply a dll file that has the extension changed to "w2p".
There are currently only 3 hook points, although I may add more in the future if there is a demand.

The hook points can be accessed by exporting appropriately named functions from a plugin dll.

w2p_init has no arguments and is called when the exe is first started.

create_game will be passed a pointer to the game name and called when the player creates a game.

join_game will be passed a pointer to the game name and called when the player joins a game.

screen_update will be passed a value that describes the current game screen and will be called immediately before each screen update.

... so to make a simple plugin all you need to do is make a dll file that exports one function called "w2p_init" that takes no agruments, then rename it to "MyPlugin.w2p" or whatever and put it into the "plugins" folder. Your plugin will then be injected into the WC2 process when you next run it.

I will try to do some examples later when time allows.


   ---------



There are 4 example plugins included. If you don't want any of them, you can disable/enable them individually just by moving them in or out of the plugins folder (of course exit WC2 first then restart).





   ----------

   The "Lobby Map" plugin






The "lobby map" Plugin gives you a preview of the map in the pre-game lobby.

  -----------------


   The "Game Time" plugin





The "Game Time" plugin just adds a small game timer to the in game screen.


     --------------



   The "Chop Bars" plugin





The "Chop Bars" plugin gives you an on-screen progress bar for chopping peons that you own and are selected.


       ------------



   The "Allied Colors" plugin





The "Allied Colors" plugin give you the option of displaying all allies in blue and all enemies in red on the mini-map.
Press F2 to toggle this on/off. It is off by default.

   ----------------------


This is still in development, but appears to be working and stable for me .... please test! :)



                                                               :critter:



Changelog
-------------
0.1 initial hosting support
0.1.1 plugin framework
0.1.2 screen_update hook
0.1.3 compatability fix for later OS
0.1.3a fix for timer plugin glitch
0.1.3b fix for chop bars in single player mode
          added CPU saver plugins


« Last Edit: July 14, 2019, 11:43:36 AM by Lambchops »
its gooder to hax hard and NEVER get caught!