VOGONS

Common searches


First post, by gabonator

User metadata
Rank Newbie
Rank
Newbie

Hello guys,

few years ago I made an experiment where I translated the disassembled code of a game (Alley Cat, Star Goose) automatically into C++ or Javascript. It was just a proof of concept and a lot of things needed to be fixed by hand. I sporadically returned to this experiment to improve the translation process. Now it has became quite powerful and standalone and here are some results:

Star goose on iphone in swift: https://youtu.be/IaZiQrUdR9s
Star goose in javascript: https://rawgit.valky.eu/gabonator/Work-in-pro … oose/index.html
Cd-man in wasm: https://rawgit.valky.eu/gabonator/Projects/ma … /web/index.html
Rick dangerous 2 in javascript: https://l.valky.eu/dosrick2
Alley cat (not fully working): https://rawgit.valky.eu/gabonator/Work-in-pro … 17/js/test.html
Conversion details: https://github.com/gabonator/Education/tree/m … 2021/CicoParser

Anyone who would like to contribute to this project? It is written in C++ and javascript and there are still a lot of things to be improved. Currently I am finetuning the conversion tool to port the Xenon2 game and I am looking to extend the compatibility to Borland C applications. I am also looking for unprotected executables for these games: storm lord, titus the fox, arkanoid2

This post was originally posted on dosgames forum

Gabriel

Reply 1 of 26, by xcomcmdr

User metadata
Rank Oldbie
Rank
Oldbie

Hello,

Very, very interesting !

Can it work on Dune (floppy version, for 286 without FPU) ?

I should contribute indeed !

I have for starters a single exe (LOGO.EXE, which reads and plays LOGO.HNM), with LZ compression removed (the executable was LZEXEd with the tool by Fabrice Bellard).

Reply 2 of 26, by gerry

User metadata
Rank Oldbie
Rank
Oldbie
gabonator wrote on 2021-02-11, 21:40:
Hello guys, […]
Show full quote

Hello guys,

few years ago I made an experiment where I translated the disassembled code of a game (Alley Cat, Star Goose) automatically into C++ or Javascript. It was just a proof of concept and a lot of things needed to be fixed by hand. I sporadically returned to this experiment to improve the translation process. Now it has became quite powerful and standalone and here are some results:

Star goose on iphone in swift: https://youtu.be/IaZiQrUdR9s
Star goose in javascript: https://rawgit.valky.eu/gabonator/Work-in-pro … oose/index.html
Cd-man in wasm: https://rawgit.valky.eu/gabonator/Projects/ma … /web/index.html
Rick dangerous 2 in javascript: https://l.valky.eu/dosrick2
Alley cat (not fully working): https://rawgit.valky.eu/gabonator/Work-in-pro … 17/js/test.html
Conversion details: https://github.com/gabonator/Education/tree/m … 2021/CicoParser

Anyone who would like to contribute to this project? It is written in C++ and javascript and there are still a lot of things to be improved. Currently I am finetuning the conversion tool to port the Xenon2 game and I am looking to extend the compatibility to Borland C applications. I am also looking for unprotected executables for these games: storm lord, titus the fox, arkanoid2

This post was originally posted on dosgames forum

Gabriel

this is wonderful

for those willing to delve into the derived code it is possible to create new features, fix old bugs and so forth, something often very difficult to do editing binaries directly

Reply 4 of 26, by gabonator

User metadata
Rank Newbie
Rank
Newbie

It is still not ready for use by "general audience", but here is the code:
https://github.com/gabonator/Projects/tree/ma … r/CicParser2021

Sample configuration file, input assembly and generated code is here:
https://github.com/gabonator/Projects/tree/ma … ion/Parser/test

Host application (SDL2 in xcode) is here, you can see there ega.h which emulates EGA display, cpu.h implements memory, registers, some assembly instructions, actual code of the game is in rick2.h. It is still very messy code, but quite compact and easy to understand
https://github.com/gabonator/Projects/tree/ma … n2/CicParserSim

When the game is running fine in host application, we can convert it into javascript. Originally the cicoparser handled this, but later I dropped this option, because otherwise you would need to fix every bug twice (there are still some 1% instructions in the produced file which need to be fixed manually). Using following script you can convert C code into javascript:
https://github.com/gabonator/Projects/blob/ma … nvertJs/conv.js

And resulting game in javascript (rick dangerous 2):
https://github.com/gabonator/Projects/tree/ma … ection/WebRick2

Most clean and representative project in C/C++ is this one (Cd-Man running as WASM in web browser) with nicely decoupled interfaces, but some cleanup should be done though
https://github.com/gabonator/Projects/tree/ma … asmCdman/source

This file nicely demonstrates the benefits of this approach (conversion instead of emulation), following file implements touch control for the CD-Man with simple path finding algorithm. Cdman is a pac man clone which is controlled using keyboard or joystick. With this simple code it is possible to tell the pacman where to go just by tapping/clicking any point in the game map and it automatically calculates the shortest path:
https://github.com/gabonator/Projects/blob/ma … an/web/extra.js

This is just my side project I do in free time, so the code is written in a quick and dirty fashion with a lots of copy-pastes. When I will finish conversion of Xenon2 I will do major cleanup and possibly completely reorganize the repository structure

Reply 6 of 26, by gabonator

User metadata
Rank Newbie
Rank
Newbie

Wow, interesting. I knew that few games have been ported by converting assembly code into C and this guy has made something quite similar as my project, but if I am correct he has only ported music player/editor and stunts game. His approach is more universal and safer - he emulates all the flag updates made by CPU where as my approach is producing more readable code by utilizing CPU flags only in places where it is necessary. I am a bit disappointed now, I thought that no one was working on universal source to source compiler for dos applications before, my life is ruined now 😀

Reply 7 of 26, by xcomcmdr

User metadata
Rank Oldbie
Rank
Oldbie

Hey the more options the better it will be !
I'm trying both at the moment, with a few problems (mas2mc: can't compile the C code 'as is' but that's expected. With your project, OUTSB and LODS are not recognized. 🙁 )
I'm using IDA 5 freeware if that's important.
But perhaps my RE efforts should be in another thread.

Reply 9 of 26, by xcomcmdr

User metadata
Rank Oldbie
Rank
Oldbie

Here they are. 😀
OUTSB:

sub_28A8 proc near
push si
push ds
push es
pop ds
assume ds:nothing
mov si, dx
pushf ; Push Flags Register onto the Stack
cmp cs:byte_1F3E, 0 ; Compare Two Operands
jz short loc_28C6 ; Jump if Zero (ZF=1)
mov dx, cs:word_1F3C

loc_28BC:
in al, dx
and al, 8 ; Logical AND
cmp al, cs:byte_1F3F ; Compare Two Operands
jnz short loc_28BC ; Jump if Not Zero (ZF=0)

loc_28C6: ; Clear Interrupt Flag
cli
mov dx, 3C8h
mov al, bl
out dx, al
jmp short $+2 ; Jump
jmp short $+2 ; Jump
jmp short $+2 ; Jump
jmp short $+2 ; Jump
inc dx ; Increment by 1
mov ax, cx
add cx, cx ; Add
add cx, ax ; Add
cmp cs:byte_1F3B, 0 ; Compare Two Operands
jz short loc_28EA ; Jump if Zero (ZF=1)
rep outsb ; Output Byte(s) to Port
popf ; Pop Stack into Flags Register
pop ds
assume ds:dseg
pop si
retn ; Return Near from Procedure

loc_28EA: ; Load String
lodsb
out dx, al
loop loc_28EA ; Loop while CX != 0
popf ; Pop Stack into Flags Register
pop ds
pop si
retn ; Return Near from Procedure
sub_28A8 endp

LODS:

sub_2D7D proc near
push si
les si, dword_30E6 ; Load Full Pointer to ES:xx

loc_2D82: ; Load String
lods word ptr es:[si]
mov cx, ax
sub cx, 2 ; Integer Subtraction
pop si
or ax, ax ; Logical Inclusive OR
retn ; Return Near from Procedure
sub_2D7D endp

Reply 10 of 26, by Jorpho

User metadata
Rank l33t++
Rank
l33t++
gabonator wrote on 2021-02-11, 21:40:

I am also looking for unprotected executables for these games: storm lord, titus the fox, arkanoid2

I'm certain I've read something about defeating the protection on Titus games. Alas, Google fails me.

Reply 11 of 26, by gabonator

User metadata
Rank Newbie
Rank
Newbie

Just small hint for now, when I will have some time I will push support for these two into the repository:

rep outsb is a string operation. All classes responsible for parsing assembly string instructions are in matchers.h, and we are looking especially for CIMStringOp:

class CIMStringOp : public CInstructionMatcher
{
virtual shared_ptr<CInstruction> Match(string strLine) override
{
vector<string> arrMatches;

if ( CUtils::match("^(rep|repne|repe)\\s+(stosb|lodsb|stosw|lodsw|movsw|movsb|scasw|scasb|cmpsb)$", strLine, arrMatches) )
{
return make_shared<CIString>(CIString::GetRule(arrMatches[0]), CIString::GetOperation(arrMatches[1]));
}

return nullptr;
}
};

To add support for "rep outsb" we will just add it into the regex like this:
if ( CUtils::match("^(rep|repne|repe)\\s+(stosb|lodsb|stosw|lodsw|movsw|movsb|scasw|scasb|cmpsb|outsb)$", strLine, arrMatches) )

and then we add string operation (outsb) in CIString::GetOperation and CIString::EOperation:

	static EOperation GetOperation(string str)
{
CheckEnum(stosb); CheckEnum(lodsb);
CheckEnum(stosw); CheckEnum(lodsw);
CheckEnum(movsw); CheckEnum(movsb);
CheckEnum(scasw); CheckEnum(scasb);
CheckEnum(cmpsb); CheckEnum(outsb); // <---- this one
_ASSERT(0);
return invalidop;
}

And finally for export into C, you will need to implement support in CCStringOp

	CCStringOp(shared_ptr<CIString> pInstruction)
{
switch (pInstruction->m_rule)
{
case CIString::rep: m_strRepeat = "rep"; break;
case CIString::repne: m_strRepeat = "repne"; break;
case CIString::repe: m_strRepeat = "repn"; break;
default:
_ASSERT(0);
}

switch (pInstruction->m_operation)
{
case CIString::lodsb: m_strOperation = "lodsb"; break;
case CIString::stosb: m_strOperation = "stosb"; break;
case CIString::lodsw: m_strOperation = "lodsw"; break;
case CIString::stosw: m_strOperation = "stosw"; break;
case CIString::movsb: m_strOperation = "movsb"; break;
case CIString::movsw: m_strOperation = "movsw"; break;
case CIString::scasb: m_strOperation = "scasb"; break;
case CIString::scasw: m_strOperation = "scasw"; break;
case CIString::cmpsb: m_strOperation = "cmpsb"; break;
case CIString::outsb: m_strOperation = "outsb"; break; // <--- this one
default:
_ASSERT(0);
}

switch (pInstruction->m_operation)
{
case CIString::lodsb: m_strTemplate = CCZeroArgOp::GetTemplate(CIZeroArgOp::lodsb); break;
case CIString::stosb: m_strTemplate = CCZeroArgOp::GetTemplate(CIZeroArgOp::stosb); break;
case CIString::lodsw: m_strTemplate = CCZeroArgOp::GetTemplate(CIZeroArgOp::lodsw); break;
case CIString::stosw: m_strTemplate = CCZeroArgOp::GetTemplate(CIZeroArgOp::stosw); break;
case CIString::movsb: m_strTemplate = CCZeroArgOp::GetTemplate(CIZeroArgOp::movsb); break;
case CIString::movsw: m_strTemplate = CCZeroArgOp::GetTemplate(CIZeroArgOp::movsw); break;
case CIString::scasb: m_strTemplate = ""; break;
case CIString::scasw: m_strTemplate = ""; break;
case CIString::cmpsb: m_strTemplate = ""; break;
case CIString::outsb: m_strTemplate = ""; break; // <---- this one
default:
_ASSERT(0);
}
}

Reply 12 of 26, by VileR

User metadata
Rank l33t
Rank
l33t

Impressive work!

For now, just a little contributory heads-up: the 16-color RGBI palette (CGA/EGA) looks wrong/too saturated; it should use the values seen in the last table on this page.

About Stormlord/Titus/Arkanoid 2, don't the usual suspect 'abandonware' sources have de-protected executables? (no links here). May be able to help privately if you're still having trouble finding them.

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 14 of 26, by gabonator

User metadata
Rank Newbie
Rank
Newbie

@VileR, thank you very much for this. I always thought something is wrong with my palette
@xcomcmdr, here are necessary changes for the cicoparser to support rep outsb and lods instructions:
https://github.com/gabonator/Projects/commit/ … ca98c9c30f4abd9

generated code from the two funcions you provded:

void sub_28A8();
void sub_2D7D();

void sub_28A8()
{
WORD _cs = _seg000;
_push(_si);
_push(_ds);
_push(_es);
_ds = _pop();
_si = _dx;
_pushf();
if (memory(_cs, 0x1F3E) == 0x00)
goto loc_28C6;
_dx = memory16(_cs, 0x1F3C);
loc_28BC:
_in(_al, _dx);
_al &= 0x08;
if (_al != memory(_cs, 0x1F3F))
goto loc_28BC;
loc_28C6:
_flags.interrupt = false;
_dx = 0x03c8;
_al = _bl;
_out(_dx, _al);
_STOP_("goto $+2");
_STOP_("goto $+2");
_STOP_("goto $+2");
_STOP_("goto $+2");
_dx += 1;
_ax = _cx;
_cx += _cx;
_cx += _ax;
if (memory(_cs, 0x1F3B) == 0x00)
goto loc_28EA;
_rep_outsb();
_popf();
_ds = _pop();
_si = _pop();
return;
loc_28EA:
_lodsb<MemAuto, DirAuto>();
_out(_dx, _al);
if (--_cx)
goto loc_28EA;
_popf();
_ds = _pop();
_si = _pop();
}
void sub_2D7D()
{
_push(_si);
_les(_si, _ds, 0x30E6);
loc_2D82:
_lods(memory16(_es, _si));
_cx = _ax;
_cx -= 0x0002;
_si = _pop();
_ax |= _ax;
}

If there wont be any other problems, you can put generated code into the host app. I suggest using this one since it is most recent:
https://github.com/gabonator/Projects/tree/ma … n2/CicParserSim

just replace the code in rick2.h file

Then you will need to implement the _rep_outsb() and _lods methods. Reading the documentation it should look like this:

void _rep_outsb()
{
_ASSERT(_cx && _flags.direction == 0);
while (_cx--)
_out(_dx, memory(_ds, _si++));
_cx = 0;
}

void _lods(WORD& w)
{
_ax = w;
}

void _lods(BYTE& b)
{
_al = w;
}

Regarding the _STOP_ calls, you can delete them, they are there just for giving more time for the graphics adapter. If it is some sort of graphics effect, you can put there _sync() call which will display the video buffer on screen. Last line in sub_2D7D tells that some comparison is ongoing after calling of this function. Unfortunately this needs to be fixed by hand. The parser should drop there if (_FIXME_) code which stops program execution and forces you to fix the problem.

Reply 15 of 26, by BardBun

User metadata
Rank Member
Rank
Member

How much of the code does this make readable?

Just wondering, because if it would allow insight into a games code proper it would be really easy to create those OpenSource Shell programmes to run the games in as accurately as possible.

Reply 16 of 26, by xcomcmdr

User metadata
Rank Oldbie
Rank
Oldbie

Thank you so much ! It is very kind of you to have taken the time to implement this. I'll try it ASAP (and talk more what about I'm trying to do in another thread).
It's indeed a graphical animation with no sound (read from LOGO.HNM data file) -> https://www.youtube.com/watch?v=KR8eo6QltCU

Reply 19 of 26, by gabonator

User metadata
Rank Newbie
Rank
Newbie

Porting the first game was most difficult. Because you are looking at the assembly instructions and sometimes you are not sure what they mean. Even little mistake means that the game wont work. To convert new game means to add support few new instructions and to solve new programming tricks. The biggest issue for xenon2 is indirect addressing. When new enemy appears on the screen, a structure is added to a linked list of visible objects. Every object has a handler that is called when the enemy is redrawn on the screen or hit. This handler is just a number - offset in the memory where the drawing method is placed. Since IDA analysis does not find any reference to this method, it treats is a data block instead of code. So you just play the game, and the game will crash/stop at a point, when some new enemy is displayed. For you it means, you will open IDA, jump to the specific address of the game and convert the data block into code. Then export to file, then use cicoparser to export just this new method and add it to your code and compile the game again, and play the level for the 1000-th time until something similar happens again. So that is the reason why I converted just the first level (and half of the second level). Because it is too time consuming and quite boring. At the end you will get list of indirect methods like here:
https://github.com/gabonator/Projects/blob/ma … 2/host.html#L19

But it has it's benefits - you know which method is responsible for displaying enemies, for adding new power ups, for shooting... so at the beginning of the first level I made a little hack to give you cannon power up along with the rear shot, so it is more fun to play it 😀