Welcome, Guest | Home | Search | Login | Register
Author Notes on using CodeWarrior to port games, etc. (Read 108615 times)
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
on: January 29, 2025, 04:51

I've ported a handful of games to Classic MacOS lately, posting them to MG when deemed fit for the public.  (And have several more in the pipeline). 

These are games written with SDL 1.x, originally to test if my m68k port was a viable idea or not.  Did my port actually work?  Was it fast enough on real hardware to be actually useful?  Happily the answer to both was "Yes".  (With the caveat that games that update the entire screen and are graphically intensive will always run too slowly...but they do run correctly, so good in Basilisk...but of course OpenGL is out...for now...)

My focus was on testing my m68k SDL, but, I, of course build everything on PPC first, since SDL is "known to work" there.  So there are now a few "new" games for System 7/8/9 PPC also at MG.

In the process I keep running into the same problems and gotchas over and over, and started taking very sparse notes, just to remind myself how I worked around them.  It occurred to me these would also be useful to anyone else attempting a port, so I'm going to dump them here, and will expand and add explanations, as time permits or if anyone is curious about them.

These don't cover writing entirely new Mac backends, it is assumed the software SHOULD be buildable, all the needed libs are already available.  This also won't cover basic Mac porting issues, like dealing with "/" in filenames, not having a home directory, etc etc, although I'll touch on them when them come up.  All of this is also applicable to Carbon apps on MacOS 9 (ie "CFM" not "Mach-O").

I've been using CodeWarrior 6 and 7 Pros, so some of these are specific to them, but many are just general CodeWarrior things.  If you use an older version, the problems will be worse.  If you use a newer version they may be better, but you'll likely still run into some of them.

This will NOT cover or discuss SDL, but issues I ran into with the game code itself.

So...on with the show...
Last Edit: January 29, 2025, 05:47 by lauland
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #1 on: January 29, 2025, 05:03

First, for those curious, and who haven't been able (or wanted) to follow the volumes of discussion @Jatoba and I have had in the SDL thread, here are the games I've ported:

http://macintoshgarden.org/games/breaker-0
"Arkanoid" clone, runs fine on PPC, but way too slow on real m68k hardware, and not much better in Basilisk!

http://macintoshgarden.org/games/sdlroids
"Asteroids" clone, runs very well, even on real m68k hardware.

http://macintoshgarden.org/games/sdl-scavenger
"Lode Runner" clone, runs well on PPC.  No m68k binary supplied because it crashes, but runs well...before that...on real hardware. Haven't been able to figure out what is going wrong.

http://macintoshgarden.org/games/snoopy-vs-the-red-baron-2001
Play as "Snoopy" (aka Generic White Dog for copyright reasons) in air combat against the Red Baron, either AI, or with a friend sharing the keyboard.  Runs well on both PPC and real m68k hardware.

http://macintoshgarden.org/games/labbaye-des-morts
"Spectrum-style" game, actively in progress, currently crashing on PPC no no binary yet, but there is a screenshot.  Should run well on real m68k's once...or if...I get it stable.

All the my .sit files at MG include the full source code with my needed patches, and CW projects, for both CW6 and 7, and both m68k and PPC.

NOTE: None of the m68k versions have sound, as audio in my m68k SDL port is crashing.

----

I'm also looking at "Berusky", "FreeDroid Classic", "LBreakout", and "SDL Ball", although dealing with CodeWarrior limitations with all of them, some serious enough I may never get them working.

If you know of any other games written using SDL 1.x (And NOT SDL2) that you might like to play on MacOS 7/8/9, let me know and I'd be happy to take a look...

Now...on to CodeWarrior...
Last Edit: January 29, 2025, 05:46 by lauland
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #2 on: January 29, 2025, 05:12

These are my rough CW notes.  I've run into these when porting... I've had to get around all of them one way or another:

Code may use names of vars, funcs, etc that conflict with MacOS API ones. ("DrawLine", "Window"), etc.

Be sure not to not accidentally include test apps ("main redefined", etc).

Does not allow you to define global vars multiple times, and just the linker sort it out. Use extern everywhere but single place where defined.

Does not support init'ing structs with dot syntax like "MyColor c={.r=0,.b=0,.g=0}".

Does not support =| and other math operators on non ints.

Can't define array with variable or calc'd size as local var.

Confusion over the return val of malloc etc.

malloc confusion is actually just void ptrs in general...even with "relaxed pointers" you will need casts.

Missing or radically different include files like stat.h or time.h or dirent.h etc (Most "sys" includes).  Or those that are there will be missing "standard" things.

Some of the "missing" defines are just in the "wrong" (aka different) files...things that should be in "types.h" are in "stat.h", etc.  ("off_t" or "ssize_t", etc).

Pickier about const char *, and mixing const in general.

Can't define struct of structs (or struct with arrays) with values in nested curly brackets.

Other compilers allow C++ish features in C (such as skipping typedefs sometimes, or just the syntax of it).

Some compilers allow variable names that are same as keywords in C++ etc ("class"), if the syntax is clear enough what you mean to be doing.

Missing strdup, or must use _strdup.

Missing strncasecmp and other similar more obscure string funcs.

ctype.h "funcs" are really just defines.

Far pickier about function pointers...MUST match params, esp when passing them as function params themselves

Pickier about ptr arith, will likely need to cast to ints, then cast results back to ptr.

No getopt...because there is no command line.

No getenv or putenv, because there are no environment vars.

Be on the lookout for file paths with things like "../" in them.

Missing M_PI, not in math.h sometimes.

Will get confused if you have two .c files with the same name but in different folders in a project for different targets or even if not in your target.

If you have two headers/include files with the same name, it may randomly "find" the wrong one for you.  Be sure to remove ones not really using.

Depending on "Access Paths" it may find something like "sys/time.h" (or "whatever/time.h") when you just have "#include <time.h>".

All standard "unix" socket and tcp related headers and funcs missing, must use GUSI (which sits on top of OpenTransport and MacTCP).

Some signals like SIGARLM are missing or in the "wrong" include file.

Standard "microsecond" delay func missing...need to use MacOS API version...

Missing alloca, but can almost always just use calloc/malloc, etc.
Last Edit: January 29, 2025, 05:53 by lauland
68040
512 MB
*****
Posts: 950
68k - thy kingdom come, thy will be done !
View Profile
Reply #3 on: January 29, 2025, 12:07

Can we make posts like this a "permanent" in the Development forum?
Jatoba
256 MB
*****
Posts: 270
System 9 Newcomer!
View Profile
Reply #4 on: January 29, 2025, 21:34

Quote from: lauland
If you know of any other games written using SDL 1.x (And NOT SDL2) that you might like to play on MacOS 7/8/9, let me know and I'd be happy to take a look...

All this while, I never realized a certain game was made in SDL 1.2 (and SDL_mixer, SDL_image and zlib, at least):

Super Mario War

It's actually pretty fun, simple and addictive at the same time, with multiplayer support up to 4 players. Had great fun with it circa 2006! Like a true cross-platform SDL game, it was compiled for lots of things: OS X, Windows, PSP, GNU/Linux, Nintendo DS (I think) etc..

If you think it's worth a try, I think the latest codebase was mirrored here. The game also contains a level editor, which I think is compiled separately as another target.

Should be a very light game, but I'm not sure how well 68k would fare for it. It might have a good chance at performing well, as it doesn't use OpenGL nor anything "too heavy" AFAIK, but who knows. But on PPC System 7 ~ Mac OS 9, it'd definitely be excellent.

----

It goes without saying at this point, but thank you also for sharing all these CW notes, they really help guide the ship. And of course, thanks again for each and every Mac port!!
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #5 on: January 30, 2025, 04:02

Excellent find @Jatoba!  Looks really fun!

If the the background scrolls, then it will be updating the screen every frame, so will probably be far too slow on 68k machines...but doesn't mean I won't try!  And, of course, PPC first...if that's all we end up getting out of it, we'll still have gotten an awesome "new" game!

At first glance, they mention they used an SVN server...oh no, that wouldn't have been captured by archive.org...but there's a link further down to fileden.com...dang!  Also not captured...but a quick google finds this:

https://github.com/mmatyas/supermariowar
(modern browser)

Hmm...

"In 2004, Florian Hufsky, founder of the 72dpiarmy forum started working on an open-source rewrite, which became Super Mario War..."
"...At the end of 2009, Florian died. The development of the game slowed down and eventually stopped..."
"...This is a fork I've started working on around 2014..."
"...Main changes since 1.8..."
"...Ported all parts of the game to SDL2..."

Grr....!  But...
"Old releases for Mario War and SMW can be found here."
https://github.com/mmatyas/supermariowar/releases/tag/1.8
(modern browser)
Thank God!

So, it looks like 1.8b2 was the last version from Florian, and it should use SDL 1.2.  I'll download that, and document my steps here...

----

"L'Abbaye Des Morts" update:
It fully builds, displays the title screen, plays music, and when you start the game shows a little intro with some text and some bad guys chase your hero across the screen...then hangs!

On real PPC hardware, it crashes to Macsbug, but like SDL Scavenger on 68k, it has trashed its stack, and probably the heaps also, so can't tell where exactly it crashed.  It has "jumped to address zero", so likely tried to follow a null function pointer.  Running Debug in CodeWarrior goes to the same place, again, with no info.

I added "debug printf"s to all major functions which allow me to see where it is hanging and it is puzzling.  It is crashing inside of SDL_Flip(), part of SDL.  I can see a debug printf right before, but never see the one after.  Commenting out the SDL_Flip allows the game to "run", but nothing is displayed.  So culprit found.  This means it either corrupted the SDL surface, or memory otherwise BEFORE that.

----

An aside on how games in general work: This is for games that update the entire screen per frame, but the same things happen even if you only redraw parts.

You have the entire screen in memory as a bitmap.  You also have other bitmaps that are the offscreen parts of the "world".  You usually have one just for the background, and then others for the characters, enemies, and parts that are different for each level...like doors, pieces of the floor, etc etc etc.  "Abbaye" happens to have a single bitmap with EVERYTHING other than the background.  This is sometimes called a "sprite sheet", google that and you'll see MANY good examples...

You copy different rectangles of the sprite sheet onto the "entire screen" bitmap.  To do animation, you'll have several copies, of a little guy for example, and you copy different ones each time, and it looks like the feet are moving, etc.  You put the moving objects (the character, enemies etc) in different places, as their coords are updated, etc.  So you assemble how the entire screen will look all at once but it behind the scenes offscreen.

Then...WHAM!  You copy the entire screen at once and it is visible.  You do this once per frame.  Check events to see for joystick or keypresses.  Then update variables that represent the character and enemies and everything else that changes...repeat!  This is called "the game loop". 

In games that use SDL, these offscreen bitmaps are called "surfaces", and the "WHAM!" step is a call to SDL_Flip().  On other platforms or using other libraries, everything works the same way, just different names.  Almost every game in the world does these kinds of things.

----

Next steps for "Abbaye": First things first...if I didn't trust the version of SDL I'm using, I'd suspect a problem in SDL itself and start looking there.  But I DO trust it, since if it were that broken, I'd have noticed in other games...  There's also the point that the "intro" screen draws the character and enemies moving fine...so I can trust that the problem is NOT SDL_Flip() itself.

From the debug printfs I can see it crashes on the very first frame it tries to draw.  Which is good and bad...it means something is badly going wrong...maybe easier to find, but harder to debug.  Total crashes are good in that more subtle issues can take A LOT of time to find.  There are two ways to proceed...the "smart way" and the "dumb way"...

The "smart way" would be to set breakpoints in some of the functions so the game runs, then stops exactly where you tell it and you're in the CodeWarrior debugger.  There you can look at structs and variables and see their state...does everything look good to you?  Hmm...  If it does, put other breakpoints, repeat...  This works very well if you have a good debugger (CodeWarrior's is IMHO "decent" but not great), and understand the code.  Perfect if you actually wrote the game! 

Since I didn't write "Abbaye" it'd require me to learn the code.  The original author(s) spoke Spanish, and so the variables and functions have names in that language same with all comments.  I speak enough to understand THAT...but still...I'd have to actually "read his mind" and understand what each function really does (Just "MoveEnemy" doesn't tell you what it actually does, or how).  Doing this in general (Spanish aside) is not easy and takes time and effort...it requires you to put yourself in the author's shoes, or at least recognize patterns that are common in game programs when you can see them.

So unless the code starts "making sense" to me and I can tell what he was doing where, I'm likely to try the "dumb way".  (I can set breakpoints and look at things, but I have little idea if they "look good" or not...I guess if I see coords that "look weird", etc, but even that could be "normal").

The "dumb way" means commenting out parts of the code, until it no longer crashes.  Once that happens, I'll dig deeper.  I'll start at the top of the game loop, and comment out the bits that move the player, enemies, draw the background, etc, one at a time.  This is "dumb", but can get results quickly.  We'll see...
Last Edit: January 30, 2025, 04:35 by lauland
ShinobiKenobi
256 MB
*****
Posts: 362
System 7 fan
View Profile My personal website
Reply #6 on: January 30, 2025, 07:26

Excellent work, lauland! Keep up the good work!@
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #7 on: January 30, 2025, 16:03

So I have Super Mario War started in CodeWarrior 7.  A few of the source files already compile, as is.  Already ran into the "missing _strdup", and also "_strlower" is missing, and worked around them.

It's running into a problem with a "union", which is a mutant sort of struct that a lot of programmers don't use because, well, they're a little goofy, and basically are used to access one bit of data in (at least) two very different ways.  The union in the code has two "anonymous" structs, ie they aren't given names, but it tries to access their values without specifying WHICH of the two structs it wants.  I believe CodeWarrior doesn't support this, so am going to try giving the structs names...which then means I have to add those every time it tries to access the union.  A bit of a pain, but likely to work...
cballero
1024 MB
******
Posts: 1176
System 7, today and forever
View Profile
Reply #8 on: January 31, 2025, 15:15

This is like resurrecting old goldmines! :D things never considered portable before can now gain new life and fly right into our 68k and PPC screens; what a marvel to behold indeed! :)
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #9 on: January 31, 2025, 19:01

Super Mario Wars...

I was correct about the union problem...adding names to the structs worked...but I then had to change every time something like this showed:
OutputControl[iPlayer].game_jump;
to
OutputControl[iPlayer].oc_game.game_jump;
And
OutputControl[iPlayer].menu_down;
to
OutputControl[iPlayer].oc_menu.menu_down;

It other words, had to specify which were using the keys for "game" keys, and which for "menu" keys.  This solved about 80% of the errors.

Then...also missing "_stricmp"...just like the other _str* funcs...

They defined a couple things that looked like sprite bitmaps as arrays in one place, but then not in another...just made them the same.  Other compilers than CodeWarrior probably would let them get past that...

Then...they were very sloppy in one particular file used to read options...they'd declare some member funcs as void, but then actually used them as bool...fixed that...  And a few other sloppy things like that.  A bunch of casts were needed for reads and writes from files, etc etc.  (Other compilers let you be sloppier).

Then...they have something like "--list.atEnd()", which seems to used to find the second from last item in a list.  You can't do "--" on what they were trying to do.  I couldn't tell EXACTLY how it would work, it seems "--" isn't QUITE right, so I just commented the -- out.  This would PROBABLY make it skip the last item in the lists it was looking at.  This looks like maybe reading options from a file...I'll come back to it later.  (Normally this would mean something very similar to "list.atEnd()=list.atEnd()-1", but that doesn't make sense either).

(The point being, if I get the game running, I'll be looking out for problems involving options...or if everything seems to work otherwise, just mark it down as a limitation slash bug for someone else to fix).

----

Finally, fully compiles and I get an app.  I needed to remember to change the default memory from 5120, which is FAR too low, so just picked 32768 (32m) which should be enough for now.

Now have an app, and when I run it I get:
ERROR: Empty directory.  /gfx/packs/Classic/tilesets/
ERROR: Empty map directory!

It has its graphics in a folder called "gfx", but that slash at the beginning of the path it is looking for is the problem.  In some games you can find a string like "SOMETHIHNG/gfx/packs" and you'll know the problem is with the "SOMETHING" part.  That isn't the case.  Instead it is adding that slash somewhere.  I'll be hunting down where that might be next...

This kind of problem, with the game fully building, but not being able to find its data files is VERY typical of doing a port to the Mac, because of path issues, or limitations in functions dealing with directories, etc.
Last Edit: January 31, 2025, 20:04 by lauland
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #10 on: January 31, 2025, 21:16

Fixed covertPath to not add the initial slash, added error checking to the opendir calls and now...

DirectoryListing opendir(gfx/packs/Classic/tilesets/) failed!
ERROR: Empty directory.  gfx/packs/Classic/tilesets/
SimpleFileList(filters/,.txt,)...
DirectoryListing opendir(filters/) failed!
DirectoryListing opendir(maps/) failed!
ERROR: Empty map directory!

Those folders definitely exist, and aren't empty.  So one of two things is happening...either it is having trouble with the slashes in the names, and I can try converting them to the Classic Mac ":"...or CodeWarrior's opendir doesn't work or is limited in some way.  (Maybe their standard file io calls like open and fopen handle slashes, but opendir doesn't?)

Since I didn't have trouble with slashes with the other game ports (but they didn't use opendir), and know that directory functions tend to be tricky (not originally part of c library), I can probably assume opendir is limited or a little broken...so might write a test program or something like that...etc etc.

----

EDIT: Wrote a test program...

opendir(gfx) succeeded!
opendir(gfx/) failed!
opendir(gfx/packs) failed!
opendir(gfx/packs/) failed!
opendir(gfx/packs/Classic) failed!
opendir(gfx/packs/Classic/) failed!

opendir(gfx) succeeded!
opendir(gfx:) failed!
opendir(gfx:packs) failed!
opendir(gfx:packs:) failed!
opendir(gfx:packs:Classic) failed!
opendir(gfx:packs:Classic:) failed!

So CodeWarrior's opendir can't handle subdirs...it wouldn't be easy to fix it...so I could simplify SMW's directory structure somehow maybe...
Last Edit: January 31, 2025, 21:46 by lauland
Jatoba
256 MB
*****
Posts: 270
System 9 Newcomer!
View Profile
Reply #11 on: January 31, 2025, 21:28

@lauland Can't believe how close you already are to getting Super Mario War running! That too while also making the time to leave all those notes for us. I keep repeating myself on this, but I learn so much from them.

I will see if I can put these notes into practice myself tomorrow, and try porting... something... something which I never realized (again) was done in SDL + OpenGL all along... An amazing game, an RPG, just BEGGING to be ported... I will write more on it later, and see if I'm ACTUALLY able to port something myself right now... Or if I'm still too green (case in which I hope to get good experience, and share it).
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #12 on: February 01, 2025, 02:56

Okay...back to "Abbaye"...

Debug output...the function names are in Spanish, any English you see if mine...some rough translations juego=game, cargar=load, dibujar=draw, gestion=manage, barredeestado=status bar, cambio=change, buscar=search, habitation=level, pergaminos=scrolls, tocar=touch, pantalla=screen, etc.

iniciar_sdl done...
About to enter loop...
intro1...
About to while...
intro1 cleaning up...
intro1 done
intro2...
About to while...
intro2 cleaning up...
intro2 done
Going to juego
juego...
About to TTF_OpenFont...
About to cargardatos...
cargardatos...
reading map...
reading enemies...
cargardatos done.
About to cargar_musica...
cargar_musica...
cargar_musica done
About to while...
entering game loop...
animitems...
dibujarfase...
barradeestado...
dibujarjean...
Gestion de Jean
colisiones...
moverjean...
tocarobjetos...
contacto...
eventos...
eventos done
cambiopantalla...
cambio > 0
buscarenemigos...
musica...
musica done
habitacion!=4
habitacion!=4 done
Pergaminos...
SDL_Flip

So I'm going to start commenting out bits between the start of the game loop and the SDL_Flip...if I'm lucky, this EXTREMELY dumb ham fisted method may help locate where the problem is...
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #13 on: February 01, 2025, 04:38

Nope!  Well...sorta...  Dang was I wrong about what was going wrong!

First, the map and data files had unix linefeeds, but the MSL fgets expected them to have mac ones.  I figured this out when I found "cargadatos" was the function trashing memory...with that solved, still crashed...

THEN...I noticed the way it was passing the arrays for the screen and enemies to cargadatos was weird.  It didn't have all the dimensions.  Changed that...still crashing...

Had an insight, and instead of passing the arrays made them just global vars.  Partial success!  Now the game screen draws, we see our hero, the status bar draws, etc.  But nothing moves and the game stands still.

Getting close...
lauland
512 MB
*****
Posts: 674
Symtes 7 Mewconer!
View Profile
Reply #14 on: February 01, 2025, 17:16

Went slightly crazy and decided...well...if it CodeWarrior is having trouble passing two arrays as parameters maybe it is corrupting memory for ALL the various arrays it passes to ALL the different functions (without explicit sizes)...let's make them ALL global variables!  Pain in the butt, but did it.  Patted myself on the back for my cleverness (a bit too soon) and launched it...

Well, it now does start, plays music, and runs for a few seconds...sometimes I even see the bad guys march in...but then crashes.  At least this time not in something as simple as SDL_Flip, thank god.  But it's hard crash so can't tell where or why.

Tried the trick of commenting bits out again in the game loop, and they at least do as expected...comment out the "Jean" code and our hero isn't there anymore etc...but nothing seems to help.

Hmm...need to figure out the keypress to get into the CodeWarrior debugger or MacsBug.  Unsure what I'd look for tho if I do.  Looks like I'm going to have to end up "understanding" the code anyway...I've already had to learn far more than I wanted about how the board is stored etc etc...

So this one is ending up a lot trickier than the other ports...not sure why...

----

The carriage return slash linefeed thing with fgets still puzzles me...why didn't I run into this with the other game ports, if CodeWarior's fgets assumes Mac text files?  Chances are I was just lucky and the other games store their data as binary, or use a better way of reading then if they're text.
Last Edit: February 01, 2025, 17:18 by lauland
Pages: [1] 2 3 4

© 2021 System7Today.com.
The Apple Logo, Macintosh™, Mac OS™, and others property of Apple Computer, Inc.
This site is in no way affiliated with Apple Computer, Inc.