Skip to content

Commit

Permalink
Merge branch 'riiz'
Browse files Browse the repository at this point in the history
  • Loading branch information
natecraddock committed Apr 28, 2022
2 parents 90b8ab2 + 8c375f9 commit 9fb8d65
Show file tree
Hide file tree
Showing 89 changed files with 1,458 additions and 145 deletions.
18 changes: 0 additions & 18 deletions .clang-format

This file was deleted.

8 changes: 2 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
*build*/
.cache/
CMakeFiles
.vscode
CMakeCache.txt
*.o
zig-cache/
zig-out/
102 changes: 58 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Open Reckless Drivin'

This project is an attempt to reimplement the classic Macintosh shareware game Reckless Drivin' as a cross-platform
game. The source code of the original game was released by the author Jonas Echterhoff in 2019, but relies heavily
on deprecated Macintosh system calls for most aspects of the game. Additionally, the game data (sprites, sounds,
level data, etc.) are all LZRW3 compressed inside the resource fork of the original game, which is an additional
barrier to reimplementation.
This project is an attempt to reimplement the classic Macintosh shareware game
Reckless Drivin' as a cross-platform game. The source code of the original game
was released by the author Jonas Echterhoff in 2019, but relies heavily on
deprecated Macintosh system calls for most aspects of the game. Additionally,
the game data (sprites, sounds, level data, etc.) are all LZRW3 compressed
inside the resource fork of the original game, which is an additional barrier to
reimplementation.

The original source may be found at https://github.com/jechter/RecklessDrivin.

Expand All @@ -16,61 +18,73 @@ this in the
where I'm rewriting in Zig! I discuss my rationale behind this in more detail on
[my blog post](https://nathancraddock.com/blog/moving-to-zig/).

**TL;DR**: The game is not playable. Binary data loading, decompression, decryption, and reading is working.
Preferences are read from a file. The source code has been restructured and uses CMake. Currently working on
using SDL2 for drawing sprites.

The original source contained a file called `Data` containing the resource fork. This has been converted into
a header file (`src/include/data`) to be embedded in the executable directly. This file contains various "Packs"
of data including Apple QuickDraw images, sounds, sprites, and fonts. Work has been done to reliably read,
decompress, decrypt, and interpret the data from this resource fork.

As an indicator of progress, PPic Packs 1000 through 1008 (the Apple QuickDraw images) have been read from the
resource fork. This verifies that the lzrw-3a decompression is working, and is a exciting marker of progress!
See the images on the [wiki](https://github.com/natecraddock/open-reckless-drivin/wiki/QuickDraw-Pictures-(PPic)).

The source code layout has been reorganized, with CMake as a build-system generator.

A small test with SDL was done to open a window with the loading screen, but has been disabled for now.

The preference file reading and writing is working well now. Because I'm developing on Linux, the paths are
Linux-specific, but I have structured the code to make it easy to update when I start testing on other platforms.
The format is INI for simplicity, and the expected file path is `~/.config/open-reckless-drivin/prefs.ini`. (or
`$XDG_CONFIG_HOME/open-reckless-drivin/prefs.ini` if set). The `open-reckless-drivin` directory must exist and will not
be created if it doesn't exist.

The **name** and **code** preferences are read for checking for a registered copy of the game, allowing for
decryption of levels 4 through 10.

Currently I am adding game loop code one function at a time. This is a slow going process. There are many
Macintosh system calls in each file that need to either be removed because they play no role in the game,
or the functions need to be replaced with a cross-platform alternative.
**TL;DR:** The game is not playable, but a lot of work has been done on
decompressing, decrypting, and interpreting the bytes in the resource fork.

I started the project summer 2020. Due to the demands of university, my work has
been off-and-on since then. I recently picked the project up again since
learning Zig and I am rewriting in Zig! See [this
issue](https://github.com/natecraddock/open-reckless-drivin/issues/2) that
tracks my progress on porting everything I did in C to Zig.

Once I am caught up on what I have already done, I'll be pulling in GLFW and
OpenGL libraries to start drawing sprites to the screen!

The original source contained a file called `Data` containing the resource fork.
This has been converted into a header file (`src/include/data`) to be embedded
in the executable directly. This file contains various "Packs" of data including
Apple QuickDraw images, sounds, sprites, and fonts. Work has been done to
reliably read, decompress, decrypt, and interpret the data from this resource
fork.

As an indicator of progress, PPic Packs 1000 through 1008 (the Apple QuickDraw
images) have been read from the resource fork. This verifies that the lzrw-3a
decompression is working, and is a exciting marker of progress! See the images
on the
[wiki](https://github.com/natecraddock/open-reckless-drivin/wiki/QuickDraw-Pictures-(PPic)).

The preference file reading and writing is working well now. Because I'm
developing on Linux, the paths are Linux-specific, but I have structured the
code to make it easy to update when I start testing on other platforms. The
format is INI for simplicity, and the expected file path is
`~/.config/open-reckless-drivin/prefs.ini`. (or
`$XDG_CONFIG_HOME/open-reckless-drivin/prefs.ini` if set). The
`open-reckless-drivin` directory must exist and will not be created if it
doesn't exist.

The **name** and **code** preferences are read for checking for a registered
copy of the game, allowing for decryption of levels 4 through 10.

Currently I am adding game loop code one function at a time. This is a slow
going process. There are many Macintosh system calls in each file that need to
either be removed because they play no role in the game, or the functions need
to be replaced with a cross-platform alternative.

# Building

So far I have only built Open Reckless Drivin' on Arch Linux, but it should hopefully build elsewhere with no changes.
I prefer Ninja, but Makefiles also work fine. Suggested build instructions:
So far I have only built Open Reckless Drivin' on Arch Linux, but I see no
reason why it wouldn't work elsewhere.

```text
mkdir build && cd build
cmake -GNinja ../
ninja
zig build
```

To run tests

```
ninja test
zig build test
```

## Registration Keys

Jonas released the game for free after it was no longer viable to update it for modern systems. More information
can be found on the [game's website](http://jonasechterhoff.com/Reckless_Drivin.html). The free registration
Jonas released the game for free after it was no longer viable to update it for
modern systems. More information can be found on the [game's
website](http://jonasechterhoff.com/Reckless_Drivin.html). The free registration
information is:

Name: Free<br>
Code: B3FB09B1EB

Registration keys can also be generated in your name using a Python script.
See [the wiki](https://github.com/natecraddock/open-reckless-drivin/wiki/Decryption) for more info.
Registration keys can also be generated in your name using a Python script. See
[the wiki](https://github.com/natecraddock/open-reckless-drivin/wiki/Decryption)
for more info.
44 changes: 44 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const std = @import("std");

pub fn build(b: *std.build.Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();

const exe = b.addExecutable("reckless-drivin", "src/main.zig");

exe.linkLibC();
exe.addIncludeDir("src/c/");
exe.addCSourceFile("src/c/lzrw.c", &.{
// The default is to enable undefined behavior detection in C code. I have
// verified that the packs are all decompressed fine, so there is no need
// to sanitize. See https://github.com/ziglang/zig/wiki/FAQ#why-do-i-get-illegal-instruction-when-using-with-zig-cc-to-build-c-code
// for more details.
"-fno-sanitize=undefined",
});

exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();

const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}

const run_step = b.step("run", "Run Reckless Drivin'");
run_step.dependOn(&run_cmd.step);

const exe_tests = b.addTest("src/main.zig");
exe_tests.linkLibC();
exe_tests.addIncludeDir("src/c/");
exe_tests.addCSourceFile("src/c/lzrw.c", &.{
"-fno-sanitize=undefined",
});

exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);

const test_step = b.step("test", "Run tests");
test_step.dependOn(&exe_tests.step);
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
51 changes: 0 additions & 51 deletions scripts/generate-code.py

This file was deleted.

34 changes: 19 additions & 15 deletions src/lzrw.c → src/c/lzrw.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,26 @@ static struct compress_identity identity = {
"Public Domain" /* Vendor of algorithm. */
};

static void lzrw3a_compress_compress(uint8_t *, uint8_t *, uint32_t, uint8_t *,
static void lzrw3a_compress_compress(uint8_t *, const uint8_t *, uint32_t, uint8_t *,
uint64_t *);
static void lzrw3a_compress_decompress(uint8_t *, uint8_t *, uint32_t,
static void lzrw3a_compress_decompress(uint8_t *, const uint8_t *, uint32_t,
uint8_t *, uint64_t *);


/******************************************************************************/

struct compress_identity lzrw_identity() {
return identity;
}

/******************************************************************************/

/* This function is the only function exported by this module. */
/* Depending on its first parameter, the function can be requested to */
/* compress a block of memory, decompress a block of memory, or to identify */
/* itself. For more information, see the specification file "compress.h". */

void lzrw3a_compress(uint16_t action, uint8_t *wrk_mem, uint8_t *src_adr,
void lzrw3a_compress(uint16_t action, uint8_t *wrk_mem, const uint8_t *src_adr,
uint32_t src_len, uint8_t *dst_adr, uint64_t *p_dst_len) {
switch (action) {
case COMPRESS_ACTION_COMPRESS:
Expand Down Expand Up @@ -386,7 +393,7 @@ void lzrw3a_compress(uint16_t action, uint8_t *wrk_mem, uint8_t *src_adr,

/******************************************************************************/

static void lzrw3a_compress_compress(uint8_t *p_wrk_mem, uint8_t *p_src_first,
static void lzrw3a_compress_compress(uint8_t *p_wrk_mem, const uint8_t *p_src_first,
uint32_t src_len, uint8_t *p_dst_first,
uint64_t *p_dst_len)
/* Input : Hand over the required amount of working memory in p_wrk_mem. */
Expand Down Expand Up @@ -680,7 +687,7 @@ static void lzrw3a_compress_compress(uint8_t *p_wrk_mem, uint8_t *p_src_first,

/******************************************************************************/

static void lzrw3a_compress_decompress(uint8_t *p_wrk_mem, uint8_t *p_src_first,
static void lzrw3a_compress_decompress(uint8_t *p_wrk_mem, const uint8_t *p_src_first,
uint32_t src_len, uint8_t *p_dst_first,
uint64_t *p_dst_len)
/* Input : Hand over the required amount of working memory in p_wrk_mem. */
Expand Down Expand Up @@ -738,11 +745,10 @@ static void lzrw3a_compress_decompress(uint8_t *p_wrk_mem, uint8_t *p_src_first,

/* Check the leading copy flag to see if the compressor chose to use a copy */
/* operation instead of a compression operation. If a copy operation was */
/* used, then all we need to do is copy the data over, set the output length
*/
/* used, then all we need to do is copy the data over, set the output length */
/* and return. */
if (*p_src_first == FLAG_COPY) {
memcpy(p_src_first, p_src_first + FLAG_BYTES, src_len - FLAG_BYTES);
memcpy(p_dst_first, p_src_first + FLAG_BYTES, src_len - FLAG_BYTES);
*p_dst_len = src_len - FLAG_BYTES;
return;
}
Expand Down Expand Up @@ -873,12 +879,9 @@ static void lzrw3a_compress_decompress(uint8_t *p_wrk_mem, uint8_t *p_src_first,
/* End of LZRW3-A.C */
/******************************************************************************/

#include <stdio.h>

#include "resource.h"

/*
void LZRWDecodeHandle(Handle *handle) {
/* Need the handle size to properly decompress. */
// Need the handle size to properly decompress.
uint32_t handle_len = GetHandleSize(*handle);
unsigned char *working_mem = malloc(sizeof *working_mem * identity.memory);
Expand All @@ -897,13 +900,14 @@ void LZRWDecodeHandle(Handle *handle) {
handle_len - 4, dst_mem, &dst_len);
free(working_mem);
/* Reallocate the dst block to be exactly the right size. */
// Reallocate the dst block to be exactly the right size.
dst_mem = realloc(dst_mem, dst_len);
if (!dst_mem) {
return;
}
**handle = dst_mem;
/* Store the new handle length. */
// Store the new handle length.
SetHandleSize(*handle, dst_len);
}
*/
16 changes: 5 additions & 11 deletions src/include/lzrw.h → src/c/lzrw.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
#ifndef LZRW_H
#define LZRW_H

#include <stdbool.h>
#include <stdint.h>
#include <string.h>

Expand Down Expand Up @@ -155,11 +156,14 @@ struct compress_identity {
char *vendor; /* Where the algorithm can be obtained. */
};

/* returns id information for the compression algorithm */
struct compress_identity lzrw_identity();

void lzrw3a_compress(/* Single function interface to compression algorithm. */
uint16_t action, /* Action to be performed. */
uint8_t *wrk_mem, /* Working memory temporarily given to
routine to use. */
uint8_t *src_adr, /* Address of input data. */
const uint8_t *src_adr, /* Address of input data. */
uint32_t src_len, /* Length of input data. */
uint8_t *dst_adr, /* Address of output data. */
uint64_t *p_dst_len /* Pointer to a longword where routine
Expand All @@ -173,14 +177,4 @@ void lzrw3a_compress(/* Single function interface to compression algorithm. */
/* End of COMPRESS.H */
/******************************************************************************/

#include "defines.h"

/**
* Decompress the bytes referenced by a handle.
*
* On success, the given Resource Handle is converted to a Memory Handle and
* will need to be freed with DisposeHandle.
*/
void LZRWDecodeHandle(Handle *handle);

#endif /* LZRW_H */
Loading

0 comments on commit 9fb8d65

Please sign in to comment.