6

I've set up a DOSBox development environment with Turbo C++ intending to make a game with my friends.

I'm using C code, and am wondering how I'd link binary data into the EXE. (All my previous experience with C is libGBA, sorry if that's not actually possible in the way I think it'd be.)

If it isn't possible, then what would be an alternative option for embedding binary data? (I don't really want to need a bunch of binary files in the game directory...)

Can't find much third party documentation for Turbo C, especially considering I'm using the other, supported, but not main language for my IDE which was last updated in the early 2000s after moving to another OS entirely.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • You could always put all your data files into a single glob file with a directory at the front of it to find your data. This is actually what I did when I worked in the GBA (and GBC/DMG and DS.) – Michael Dorgan Apr 16 '19 at 07:50
  • 6
    *Why* don't you want to use files? Just about all DOS games used data files for their data. What is the real problem you try to solve by embedding the data in the executable? – Some programmer dude Apr 16 '19 at 07:51
  • Also remember that there are quite a few size limitations in DOS (even with "extenders" and such), so your executable program can't be arbitrarily large. If you have a lot of data, you *must* use multiple files. – Some programmer dude Apr 16 '19 at 07:52
  • @Someprogrammerdude idk, just seems messy to me. Can't really explain why. I feel like it might be because people seem to have had, and still have a habit of sharing just the executable without data files that go with it, or deleting some of them, thinking they're unnecessary or something. edit: That took me a few minutes to write, so i didn't see your edit. Was assuming that the binary data didn't *have* to be loaded directly into RAM. – ScottBeebiWan Apr 16 '19 at 07:55
  • 2
    I don't remember any of the pirated games (for any platform, DOS included) that didn't come with all the files needed to play them. Sometimes they were just zip-archives of the directory, sometimes they were copied floppies. I can't remember problems then or now (now about other stuff, not pirated games any more ;)). If someone who copies only the executable and not the data-files, then that's their problem, not yours (in my not humble at all opinion). – Some programmer dude Apr 16 '19 at 08:04
  • Resource data, added with 'rc'? – Martin James Apr 16 '19 at 10:26
  • 3
    It's possible to do what you want, just append your binary data to the end of the executable. For example, you could just do `copy /b foo.exe+foo.zip foo-zip.exe` and you can both run `foo-zip` as a command and use `pkzip foo-zip.exe ...` to add and delete files from the Zip file appended to the end. However, that's the extent of what you can do with existing code and utilities. You'll need to write your own code so your executable can access the appended data, whether you append it as a Zip file or in some other format. – Ross Ridge Apr 16 '19 at 18:58
  • @ScottBeebiWan using external files is the way, but if your data is manageable size you can always use `constant` tables see [Mouse program in Turbo CPP](https://stackoverflow.com/a/45561564/2521214) you just do something like this `const BYTE data[]={0,1,2,3,...};` you can also use `asm` and `db` directive. You can also write a program that reads a file and output its C/C++ constant table code with some formatting like max 128 chars per line etc ... – Spektre Apr 17 '19 at 07:59
  • 1
    @Spektre No, it won't trigger anti-virus software. It's a fairly common technique, used by self-extracting archives, certain overlay methods, and various applications that want to embed data into their executables. It's also how the New Executable format (16-bit Windows), Linear Executable format (32-bit OS/2 and VxDs), and Portable Executable format (32-bit/64-bit Windows) extend the MS-DOS MZ EXE format. – Ross Ridge Apr 17 '19 at 16:01
  • @RossRidge good to know +1 for that last comment – Spektre Apr 17 '19 at 16:27
  • @RossRidge Good idea, but how would I know where the end of the executable (the byte i'd have to seek to to read the appended data) is? – ScottBeebiWan Apr 17 '19 at 17:09
  • 1
    You'd have to parse the MZ EXE headers. See http://www.delorie.com/djgpp/doc/exe/ This problem with this technique is that you'll have to write most of the code yourself. It's simpler to just to use separate files like virtually every MS-DOS game did, including shareware and other freely distributed games. – Ross Ridge Apr 17 '19 at 17:25

2 Answers2

4

An easy solution used by programs such as self-extracting .zip files, is to simply append the data onto the end of the .exe file. The size of the .exe can be calculated from values in the header, which will give you the offset where the appended data begins. Here is an example C program that compiles with Borland Turbo C v2.01 (available as freeware) - note that I have omitted error checking for clarity:

#include <stdio.h>

int main(int argc, const char *argv[])
{
    FILE *fSelf;
    unsigned short lenFinalBlock, numBlocks;
    unsigned long offEnd;
    char trailing[256];
    int len;

    /* Open our own .exe file */
    fSelf = fopen(argv[0], "rb");

    /* Read part of the .exe header */
    fseek(fSelf, 2, SEEK_SET);
    fread(&lenFinalBlock, 2, 1, fSelf);
    fread(&numBlocks, 2, 1, fSelf);

    /* Calculate the size of the .exe from the header values */
    offEnd = numBlocks * 512;
    if (lenFinalBlock) offEnd -= 512 - lenFinalBlock;

    /* Jump to the end of the .exe and read what's there */
    fseek(fSelf, offEnd, SEEK_SET);

    /* Just read it as a string - you'd presumably be using
       some custom data format here instead */
    len = fread(trailing, 1, 256, fSelf);
    trailing[len] = 0;

    printf("Trailing data (%d bytes @ 0x%lX):\n%s", len, offEnd, trailing);

    fclose(fSelf);

    return 0;
}

Once compiled to trailing.exe you can use it like this:

C:\>trailing
Trailing data (0 bytes @ 0x2528):

I'm on Linux so I will append some example data using the shell:

$ echo Hello >> trailing.exe

Running it again shows it picking up the trailing data:

C:\>trailing
Trailing data (6 bytes @ 0x2528):
Hello
Malvineous
  • 25,144
  • 16
  • 116
  • 151
1

It should be possible to use the BGIOBJ.EXE utility which is included with Turbo C++ to achieve what you want.

BGIOBJ can convert a binary file to an .obj file which then can be linked into the .exe file. Its primary purpose is to include BGI drivers and fonts in the .exe, but it shouldn't put any restrictions on the file (except for size).

Unfortunately I can't tell you exactly how to get the memory address where the file is loaded at run-time, but that shouldn't be too difficult. BGIOBJ supports parameters for public name, segment name and segment class so you can refer to these.

idspispopd
  • 404
  • 3
  • 10
  • This causes the binary data to be loaded into memory when the program loads, which doesn't seem to be what the original poster wants. To get the memory address all you need to do refer to the public name, minus the underscore prefix, after declaring it. For example if you do `bgiobj foo.bmp foo.obj _foo_bmp` then you'd declare it with `extern char foo_bmp[];` and refer to it as `foo_bmp`. – Ross Ridge May 02 '19 at 17:49
  • Hard to say what the OP wants. I'd say as long as there is sufficient memory load everything at start is fine, but of course there is a limit what you can do in 640k. At least this is literally what "am wondering how i'd link binary data into the EXE" asks for. – idspispopd May 03 '19 at 07:09
  • The comment "Was assuming that the binary data didn't have to be loaded directly into RAM." makes it pretty clear what the original poster wants, but yah, the question as actually asked doesn't. – Ross Ridge May 03 '19 at 09:03
  • OK, I probably didn't see that comment because it was hidden. It is probably not a bad idea anyway. – idspispopd May 06 '19 at 09:16