• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

C DrawString with parameters?

BacioiuC

Well-known member
Hi guys,

I'm trying to wrap up my game and I made a ton of mistakes and problems when it comes to using strings with QuickDraw. I wanna clean things up a ton and I'm looking for some help. The only bugs left in the game are related to the log that tells you what is happening. For some reason I get weirdly erratic behaviours when using C2PStr to convert my strings to use with DrawString. So I'm asking you guys for help.

Can anyone help me out with a function that concatenates a few strings and renders them with DrawString?. Example:

DrawStringF("Creature % dealt %d damage", creatureName, damageValue) and the output is: Creature Giant dealt 4 damage.

I'm loading creatureNames and damage values from external csv files or generating them at runtime. I ported all my code before to Linux/Mac using standard C and I couldn't find any problems, so I guess it's down to how It converts. I'm looking for some help to nail a DrawString that can render in QuickDraw objects just like DrawString so I can just wrap up development.

Note: creatureName is of varied length. Can be Bat, Spider, HugeThingWithAReallyLongName.
 

cheesestraws

Well-known member
Try something like this:

Code:
#include <stdarg.h>
#include <stdio.h>

void drawCStringf(char* fmt, ...) {
    va_list aptr;
    char buffer[257];
    int clamped_size;
      
    // do the printf gubbins
    va_start(aptr, fmt);
    clamped_size = vsnprintf(buffer + 1, 256, fmt, aptr);
    va_end(aptr);
  
    if (clamped_size > 255) {
        clamped_size = 255;
    }
  
    buffer[0] = (char)clamped_size;
    DrawString((unsigned char*)buffer);
}

This requires the C dialect your version of CodeWarrior is using to be modern enough to have the snprintf family of functions. They're present in CWPro 4. Also, yes, 257 is not a typo.

If it's not up-to-date enough to have snprintf and friends, I strongly suggest upgrading. They make working with strings in C nearly bearable.
 
Last edited:

cheesestraws

Well-known member
Here's a better version. This one will work properly for strings > 128 chars. Guess why :p

Code:
void drawCStringf(char* fmt, ...) {
    va_list aptr;
    unsigned char buffer[257];
    int clamped_size;
        
    // do the printf gubbins
    va_start(aptr, fmt);
    clamped_size = vsnprintf((char*)(buffer + 1), 256, fmt, aptr);
    va_end(aptr);
    
    if (clamped_size > 255) {
        clamped_size = 255;
    }
    
    buffer[0] = (unsigned char)clamped_size;
    DrawString(buffer);
}
 

Crutch

Well-known member
Why don’t you just draw each string sequentially? DrawString automatically concatenates as it goes …. (It doesn’t add a newline at the end)

Code:
void LogStr(Str255 s)
{
    DrawString(s);
    DrawChar(' ');
}

void LogNum(long n)
{
    Str255 s;
    NumToString(n, s);
    LogStr(s);
}

...

Str255 name;
long dmg;

LogStr("\pCreature");
LogStr(name);
LogStr("\pdid");
LogNum(dmg);
LogStr(“\pdamage”);
 

BacioiuC

Well-known member
Why don’t you just draw each string sequentially? DrawString automatically concatenates as it goes …. (It doesn’t add a newline at the end)

Code:
void LogStr(Str255 s)
{
    DrawString(s);
    DrawChar(' ');
}

void LogNum(long n)
{
    Str255 s;
    NumToString(n, s);
    LogStr(s);
}

...

Str255 name;
long dmg;

LogStr("\pCreature");
LogStr(name);
LogStr("\pdid");
LogNum(dmg);
LogStr(“\pdamage”);
Wait what? You can … it continues where it left of? What?
 

BacioiuC

Well-known member
So I got everything working, Crutch's suggestion was a godsent and simplified things a ton (my previous code was trying to count the number of characters, move the pen based on the character number, split strings and all that in order to position a draw string properly. Turns out, it continues where the previous one left of so goodbye to about 50 lines of code).

Now, for the issue I was bumping into before with my old code. In Code Warrior 4 gold C2PStr converts in place. I assumed it returned a pascal string to use with DrawString. What I was trying to Draw were strings loaded from a CSV files, at run time (so people can mod the game, add creatures, items, etc). The problem?
Creature names, attack names, items were converted in-place using C2PStr when calling it inside of DrawString and that kept causing a ton of issues. I thought it was due to me using strcat and strcpy badly somewhere.

So I ended up keeping things simple.

4 functions:
Code:
void BeginLog(int pos);
void AddToLog(Str255 logMessage);
void NumToLog(int num);
void EndLog( );
.

I needed a begin and end because I keep messing with the text color, back and front and I needed to revert the changes.

For the dynamic strings loaded at run time, since you can't actually do Str255 string = GetCreatureName(i) (which is a char* _entityList[id].name) I did the following to workaround the in-place conversion (@ Cheesey's suggestions).
Code:
char buffer[50];
strcpy(buffer, GetCreatureName(i));
AddToLog(C2PStr(buffer));
AddToLog("\pattacked you for");
NumToLog(damage);
AddToLog("\pDMG");

And that's basically it. No more issues.

Thanks Crutch and Cheesey for everything :)

edit: also JCS basically has a vsnprintf working for Think-C here. I started porting the code to code warrior's syntax for quickdraw related stuff so fingers crossed. If you're using think-c you should be able to just use util.h and have access to the files directly.
 
Top