Compare commits
63 commits
jason/deve
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
35f437189e | ||
|
111b527f40 | ||
|
ffa1b89404 | ||
|
4594d22391 | ||
|
db2b3ed749 | ||
|
fe87646e62 | ||
|
19854fef80 | ||
|
189535154a | ||
|
6d9d2fa4d9 | ||
|
608cd8c1df | ||
|
ba9671360c | ||
|
9a844c15f1 | ||
|
003b83bf78 | ||
|
dbd658931f | ||
|
0b8b772b05 | ||
|
46a1d5f1c6 | ||
|
1fe91473b5 | ||
|
051c81d3d2 | ||
|
99b1ac8e6d | ||
|
862bfc5f71 | ||
|
9c43a9a247 | ||
|
bd1b158a87 | ||
|
c60a5e5a55 | ||
|
82571bca36 | ||
|
6daf22a686 | ||
|
f10172d5bb | ||
|
a67e998c28 | ||
|
43e0fe4b6e | ||
|
92894cd6d2 | ||
|
2ca7c683dd | ||
|
ce2d13ba0d | ||
|
541bd824b2 | ||
|
998bc0a0b6 | ||
|
5d24b88783 | ||
|
076397652d | ||
|
a73839c85b | ||
|
b2a7f69f57 | ||
|
3d38c8dab1 | ||
|
10c06187c1 | ||
|
13dd1200c9 | ||
|
532dc61e3b | ||
|
1956472c30 | ||
|
1ba9000661 | ||
|
aac50496f9 | ||
|
f7aa5fbee3 | ||
|
3dbcf4d7dd | ||
|
ad949fdf68 | ||
|
a93c83c32e | ||
|
6d5560a9ef | ||
|
a954a24e9c | ||
|
986c63e7e1 | ||
|
1832f5de7d | ||
|
2bcb7025c3 | ||
|
fd5875e651 | ||
|
b76e19467c | ||
|
6f88d4ae69 | ||
|
5bf7816f11 | ||
|
9a897ec8ef | ||
|
379ba1318a | ||
|
5207c8f77a | ||
|
5d5cf0fe5d | ||
|
9eb2fff01a | ||
|
b8643d14d3 |
51 changed files with 10558 additions and 31961 deletions
4
.gitattributes
vendored
4
.gitattributes
vendored
|
@ -2,3 +2,7 @@
|
||||||
*.mod filter=lfs diff=lfs merge=lfs -text
|
*.mod filter=lfs diff=lfs merge=lfs -text
|
||||||
*.vec filter=lfs diff=lfs merge=lfs -text
|
*.vec filter=lfs diff=lfs merge=lfs -text
|
||||||
*.xcf filter=lfs diff=lfs merge=lfs -text
|
*.xcf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
||||||
*~
|
*~
|
||||||
*.user
|
*.user
|
||||||
|
*.user.*
|
||||||
joeylib/lib/
|
joeylib/lib/
|
||||||
joeylib/src/SDL2/
|
joeylib/src/SDL2/
|
||||||
joeylib/src/music
|
joeylib/src/music
|
||||||
|
@ -13,3 +14,5 @@ assets/JoeyLib Logo.jpg
|
||||||
assets/JoeyLib Logo.png
|
assets/JoeyLib Logo.png
|
||||||
*.stn
|
*.stn
|
||||||
*.img
|
*.img
|
||||||
|
*.sym
|
||||||
|
*.ntp
|
||||||
|
|
BIN
assets/JoeyLib Logo.gif
(Stored with Git LFS)
Normal file
BIN
assets/JoeyLib Logo.gif
(Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
|
@ -1,456 +0,0 @@
|
||||||
/*
|
|
||||||
* JoeyLib Based Example Audio Player
|
|
||||||
* Copyright (C) 2020 Scott Duensing <scott@kangaroopunch.com>
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define JOEY_MAIN
|
|
||||||
#include "joey.h"
|
|
||||||
#ifdef JOEY_IIGS
|
|
||||||
segment "audioapp";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DMALLOC
|
|
||||||
#define DMALLOC_FUNC_CHECK
|
|
||||||
#include "dmalloc.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void help(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
|
||||||
|
|
||||||
int mo; // Mask offset
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
byte b; // Mask bit index
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
mo = ((sy & 0x1f) * 8 * 40) + (sx & 0x3f); // This is in tiles
|
|
||||||
|
|
||||||
printf("Offset: %04x sx: %02x sy: %02x\n", mo, sx, sy);
|
|
||||||
|
|
||||||
for (y=0; y<8; y++) {
|
|
||||||
b = 7;
|
|
||||||
printf("Byte: %02x ", stencil->pixels[mo]);
|
|
||||||
for (x=0; x<4; x++) {
|
|
||||||
if ((stencil->pixels[mo] & (1 << b--)) != 0) {
|
|
||||||
printf("X");
|
|
||||||
} else {
|
|
||||||
printf(".");
|
|
||||||
}
|
|
||||||
if ((stencil->pixels[mo] & (1 << b--)) != 0) {
|
|
||||||
printf("X");
|
|
||||||
} else {
|
|
||||||
printf(".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mo += 40;
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
jlDrawBlit8x8a(source, stencil, sx, sy, tx, ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Font hacking!
|
|
||||||
__attribute__((__format__ (__printf__, 5, 0)))
|
|
||||||
void fontPrint(jlImgT *font, jlStnT *stencil, jint16 cx, jint16 cy, const char *what, ...) {
|
|
||||||
jint16 x;
|
|
||||||
jint16 y;
|
|
||||||
jint16 tx;
|
|
||||||
jint16 ty;
|
|
||||||
jint16 counter;
|
|
||||||
char msg[40]; // Very short messages (screen width). Be careful!
|
|
||||||
va_list va;
|
|
||||||
|
|
||||||
va_start(va, what);
|
|
||||||
vsprintf(msg, what, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
tx = cx * 8;
|
|
||||||
ty = cy * 8;
|
|
||||||
|
|
||||||
for (counter=0; counter<(int)strlen(msg); counter++) {
|
|
||||||
x = (msg[counter] % 40) * 8;
|
|
||||||
y = (msg[counter] / 40) * 8;
|
|
||||||
if (stencil) {
|
|
||||||
#ifdef JOEY_PC
|
|
||||||
help(jlImgSurfaceGet(font), stencil, x, y, tx, ty);
|
|
||||||
#else
|
|
||||||
jlDrawBlit8x8a(jlImgSurfaceGet(font), stencil, x, y, tx, ty);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
jlDrawBlit8x8(jlImgSurfaceGet(font), x, y, tx, ty);
|
|
||||||
}
|
|
||||||
tx += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void blitTest(void) {
|
|
||||||
jlImgT *font = NULL;
|
|
||||||
jlStnT *stencil = NULL;
|
|
||||||
jint16 y;
|
|
||||||
jint16 x;
|
|
||||||
|
|
||||||
if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!");
|
|
||||||
if (!jlStnLoad(stencil, "font")) jlUtilDie("Unable to load font.stn!");
|
|
||||||
|
|
||||||
jlImgDisplay(font);
|
|
||||||
jlDisplayPresent();
|
|
||||||
jlKeyWaitForAny();
|
|
||||||
|
|
||||||
jlDrawColorSet(1);
|
|
||||||
jlDrawClear();
|
|
||||||
jlPaletteSet(15, 15, 15, 15);
|
|
||||||
|
|
||||||
fontPrint(font, NULL, 1, 2, "%s", "Blitting without stencil buffer.");
|
|
||||||
fontPrint(font, stencil, 1, 4, "%s", "Blitting with stencil buffer.");
|
|
||||||
jlDisplayPresent();
|
|
||||||
jlKeyWaitForAny();
|
|
||||||
|
|
||||||
y = 91;
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
for (x=0; x<319-8; x++) {
|
|
||||||
jlDrawBlit8x8(jlImgSurfaceGet(font), 8, 0, x, y);
|
|
||||||
fontPrint(font, NULL, 1, 6, "Drawing at %d x %d ", x, y);
|
|
||||||
jlDisplayPresent();
|
|
||||||
jlDrawBlit8x8(jlImgSurfaceGet(font), 0, 0, x, y);
|
|
||||||
jlUtilSleep(1);
|
|
||||||
if (jlKeyPressed()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jlKeyRead();
|
|
||||||
|
|
||||||
jlStnFree(stencil);
|
|
||||||
jlImgFree(font);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void exerciseAPI(void) {
|
|
||||||
|
|
||||||
jlImgT *screen = NULL;
|
|
||||||
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
jlDrawColorSet(15);
|
|
||||||
|
|
||||||
jlDrawPixelSet(10, 10);
|
|
||||||
assert(jlDrawPixelGet(10, 10) == 15);
|
|
||||||
|
|
||||||
// Should be able to grab the screen twice without allocation issues.
|
|
||||||
jlImgCreate(screen);
|
|
||||||
jlImgCreate(screen);
|
|
||||||
jlImgFree(screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void grid(void) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
jlDrawColorSet(15);
|
|
||||||
|
|
||||||
for (i=0; i<320; i+=8) {
|
|
||||||
jlDrawLine(i, 0, i, 199);
|
|
||||||
}
|
|
||||||
for (i=0; i<200; i+=8) {
|
|
||||||
jlDrawLine(0, i, 319, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
jlDisplayPresent();
|
|
||||||
}
|
|
||||||
jlKeyRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lineTest(void) {
|
|
||||||
|
|
||||||
jlImgT *kanga = NULL;
|
|
||||||
jlImgT *font = NULL;
|
|
||||||
jint16 y;
|
|
||||||
jint16 phase = 0;
|
|
||||||
jint16 x2 = 0;
|
|
||||||
jint16 y2 = 0;
|
|
||||||
jint16 ox = 0;
|
|
||||||
jint16 oy = 0;
|
|
||||||
jint16 op = 0;
|
|
||||||
jint16 color = 15;
|
|
||||||
jint16 nextColor = 1;
|
|
||||||
char what[32];
|
|
||||||
|
|
||||||
if (!jlImgLoad(kanga, "kanga")) jlUtilDie("Unable to load kanga.img!");
|
|
||||||
if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!");
|
|
||||||
|
|
||||||
jlImgDisplay(kanga);
|
|
||||||
jlDrawColorSet(1);
|
|
||||||
jlDrawBox(0, 0, 319, 199);
|
|
||||||
|
|
||||||
//jlSoundMusicPlay("music");
|
|
||||||
|
|
||||||
jlPaletteSet(15, 15, 15, 15);
|
|
||||||
strcpy(what, "Left to Right");
|
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
y = 17;
|
|
||||||
fontPrint(font, NULL, 1, y++, "Drawing %s ", what);
|
|
||||||
|
|
||||||
jlDrawColorSet((byte)color);
|
|
||||||
if (phase < 2) {
|
|
||||||
jlDrawLine(x2, y2, 319-x2, 199-y2);
|
|
||||||
} else {
|
|
||||||
jlDrawLine(319-x2, 199-y2, x2, y2);
|
|
||||||
}
|
|
||||||
ox = x2;
|
|
||||||
oy = y2;
|
|
||||||
op = phase;
|
|
||||||
|
|
||||||
switch (phase) {
|
|
||||||
// Left, Y incrementing
|
|
||||||
case 0:
|
|
||||||
y2++;
|
|
||||||
if (y2 == 199) {
|
|
||||||
strcpy(what, "Bottom to Top");
|
|
||||||
phase = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Bottom, X incrementing
|
|
||||||
case 1:
|
|
||||||
x2++;
|
|
||||||
if (x2 == 319) {
|
|
||||||
strcpy(what, "Right to Left");
|
|
||||||
phase = 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Right, Y decrementing
|
|
||||||
case 2:
|
|
||||||
y2--;
|
|
||||||
if (y2 == 0) {
|
|
||||||
strcpy(what, "Top to Bottom");
|
|
||||||
phase = 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Top, X decrementing
|
|
||||||
case 3:
|
|
||||||
x2--;
|
|
||||||
if (x2 == 0) {
|
|
||||||
strcpy(what, "Left to Right");
|
|
||||||
phase = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
jlDisplayPresent();
|
|
||||||
|
|
||||||
jlDrawColorSet((byte)0);
|
|
||||||
if (op < 2) {
|
|
||||||
jlDrawLine(ox, oy, 319-ox, 199-oy);
|
|
||||||
} else {
|
|
||||||
jlDrawLine(319-ox, 199-oy, ox, oy);
|
|
||||||
}
|
|
||||||
|
|
||||||
color = nextColor;
|
|
||||||
nextColor++;
|
|
||||||
if (nextColor > 15) {
|
|
||||||
nextColor = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jlKeyRead();
|
|
||||||
|
|
||||||
jlImgFree(font);
|
|
||||||
jlImgFree(kanga);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void musicTest(void) {
|
|
||||||
jlImgT *kanga = NULL;
|
|
||||||
jlImgT *font = NULL;
|
|
||||||
|
|
||||||
if (!jlImgLoad(kanga, "kanga")) jlUtilDie("Unable to load kanga.img!");
|
|
||||||
if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!");
|
|
||||||
|
|
||||||
jlImgDisplay(kanga);
|
|
||||||
jlDisplayPresent();
|
|
||||||
|
|
||||||
jlSoundModPlay("music");
|
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
fontPrint(font, NULL, 1, 1, "%dx%d %d %d ", jlGameGetAxis(0), jlGameGetAxis(1), jlGameGetButton(0), jlGameGetButton(1));
|
|
||||||
jlDisplayPresent();
|
|
||||||
}
|
|
||||||
jlKeyRead();
|
|
||||||
|
|
||||||
jlSoundModStop();
|
|
||||||
|
|
||||||
jlImgFree(font);
|
|
||||||
jlImgFree(kanga);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void showStencil(void) {
|
|
||||||
jlStnT *stencil = NULL;
|
|
||||||
jint16 y;
|
|
||||||
jint16 x;
|
|
||||||
jint16 count;
|
|
||||||
juint16 index;
|
|
||||||
byte bit;
|
|
||||||
|
|
||||||
if (!jlStnLoad(stencil, "font")) jlUtilDie("Unable to load font.stn!");
|
|
||||||
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
jlDrawColorSet(15);
|
|
||||||
|
|
||||||
// Draw stencil to screen linerally
|
|
||||||
index = -1;
|
|
||||||
count = 0;
|
|
||||||
for (y=0; y<200; y++) {
|
|
||||||
for (x=0; x<320; x++) {
|
|
||||||
count--;
|
|
||||||
if (count < 0) {
|
|
||||||
count = 7;
|
|
||||||
index++;
|
|
||||||
bit = stencil->pixels[index];
|
|
||||||
}
|
|
||||||
if (bit & (1 << count)) {
|
|
||||||
jlDrawPixelSet(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jlDisplayPresent();
|
|
||||||
}
|
|
||||||
jlKeyWaitForAny();
|
|
||||||
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
jlDrawColorSet(15);
|
|
||||||
|
|
||||||
// Draw stencil by pixel location - this fails on the IIgs
|
|
||||||
for (y=0; y<200; y++) {
|
|
||||||
for (x=0; x<320; x++) {
|
|
||||||
index = (y * 320 + x);
|
|
||||||
count = 7 - (index % 8);
|
|
||||||
index /= 8;
|
|
||||||
bit = stencil->pixels[index];
|
|
||||||
if (bit & (1 << count)) {
|
|
||||||
jlDrawPixelSet(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jlDisplayPresent();
|
|
||||||
}
|
|
||||||
jlKeyWaitForAny();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void stencilTest(void) {
|
|
||||||
jlImgT *kangaI = NULL;
|
|
||||||
jlImgT *biffI = NULL;
|
|
||||||
jlStnT *biffS = NULL;
|
|
||||||
jint16 y;
|
|
||||||
jint16 x;
|
|
||||||
jint16 i;
|
|
||||||
jint16 j;
|
|
||||||
|
|
||||||
if (!jlImgLoad(kangaI, "kanga")) jlUtilDie("Unable to load kanga.img!");
|
|
||||||
if (!jlImgLoad(biffI, "biff")) jlUtilDie("Unable to load biff.img!");
|
|
||||||
if (!jlStnLoad(biffS, "biff")) jlUtilDie("Unable to load biff.stn!");
|
|
||||||
|
|
||||||
jlImgDisplay(kangaI);
|
|
||||||
|
|
||||||
y = 50;
|
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
// Draw Biff & grab background
|
|
||||||
for (x=0; x<319-32; x++) {
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
for (j=0; j<4; j++) {
|
|
||||||
jlDrawSurfaceSet(jlImgSurfaceGet(biffI));
|
|
||||||
jlDrawBlit8x8(JOEY_DISPLAY, x + (j * 8), y + (i * 8), j * 8 + 32, i * 8 + 32);
|
|
||||||
jlDrawSurfaceSet(JOEY_DISPLAY);
|
|
||||||
jlDrawBlit8x8a(jlImgSurfaceGet(biffI), biffS, j * 8, i * 8, x + (j * 8), y + (i * 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jlDisplayPresent();
|
|
||||||
// Erase Biff
|
|
||||||
for (i=0; i<3; i++) {
|
|
||||||
for (j=0; j<4; j++) {
|
|
||||||
jlDrawBlit8x8(jlImgSurfaceGet(biffI), j * 8 + 32, i * 8 + 32, x + (j * 8), y + (i * 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check for early quit
|
|
||||||
if (jlKeyPressed()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jlKeyRead();
|
|
||||||
|
|
||||||
jlStnFree(biffS);
|
|
||||||
jlImgFree(biffI);
|
|
||||||
jlImgFree(kangaI);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void timerTest(void) {
|
|
||||||
jlImgT *font = NULL;
|
|
||||||
|
|
||||||
if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!");
|
|
||||||
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
fontPrint(font, NULL, 1, 1, "Timer: %d ", jlUtilTimer());
|
|
||||||
jlDisplayPresent();
|
|
||||||
}
|
|
||||||
jlKeyRead();
|
|
||||||
|
|
||||||
jlImgFree(font);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
jlUtilStartup("JoeyLib Audio");
|
|
||||||
|
|
||||||
//blitTest();
|
|
||||||
//exerciseAPI();
|
|
||||||
//grid();
|
|
||||||
//lineTest();
|
|
||||||
//musicTest();
|
|
||||||
//showStencil();
|
|
||||||
stencilTest();
|
|
||||||
//timerTest();
|
|
||||||
|
|
||||||
jlUtilShutdown();
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,7 +28,9 @@ QMAKE_CFLAGS += \
|
||||||
$$JOEY_FLAGS
|
$$JOEY_FLAGS
|
||||||
|
|
||||||
INCLUDEPATH += \
|
INCLUDEPATH += \
|
||||||
$$JOEY_INCLUDES
|
$$JOEY_INCLUDES \
|
||||||
|
/home/scott/source/lzsa/src \
|
||||||
|
/home/scott/source/lzsa/src/libdivsufsort/include
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$JOEY_HEADERS
|
$$JOEY_HEADERS
|
||||||
|
@ -38,4 +40,7 @@ SOURCES += \
|
||||||
|
|
||||||
LIBS += \
|
LIBS += \
|
||||||
$$JOEY_LIBS \
|
$$JOEY_LIBS \
|
||||||
$$SDL_IMAGE_LIBS
|
$$SDL_IMAGE_LIBS \
|
||||||
|
-llz4 \
|
||||||
|
-L/home/scott/source/lzsa \
|
||||||
|
-llzsa
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <SDL2/SDL_image.h>
|
#include <SDL2/SDL_image.h>
|
||||||
|
#include <lz4.h>
|
||||||
|
|
||||||
|
#include "lib.h"
|
||||||
|
|
||||||
#include "joey.h"
|
#include "joey.h"
|
||||||
|
|
||||||
|
@ -24,6 +26,11 @@ void convertColor(char *filename, char *basename, bool showIt) {
|
||||||
|
|
||||||
if (image == NULL) return;
|
if (image == NULL) return;
|
||||||
|
|
||||||
|
if (!image->format->palette) {
|
||||||
|
fprintf(stderr, "Not a palettized image.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
colors = image->format->palette->ncolors;
|
colors = image->format->palette->ncolors;
|
||||||
c = image->format->palette->colors;
|
c = image->format->palette->colors;
|
||||||
|
|
||||||
|
@ -56,9 +63,57 @@ void convertColor(char *filename, char *basename, bool showIt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_FreeSurface(image);
|
||||||
|
|
||||||
jlImgSave(img, basename);
|
jlImgSave(img, basename);
|
||||||
|
|
||||||
SDL_FreeSurface(image);
|
// Experimental LZ4 compression
|
||||||
|
const size_t srcSize = (sizeof(jlColorT) * 16) + (sizeof(jlPixelPairT) * 32000);
|
||||||
|
const int maxSize = LZ4_compressBound(srcSize);
|
||||||
|
char *compressedData = malloc((size_t)maxSize);
|
||||||
|
if (!compressedData) {
|
||||||
|
fprintf(stderr, "Unable to allocate space for compressed data.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int compressedDataSize = LZ4_compress_default((char *)img->palette, compressedData, srcSize, maxSize);
|
||||||
|
if (compressedDataSize <= 0) {
|
||||||
|
fprintf(stderr, "Unable to compress data.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char name[2048];
|
||||||
|
FILE *out;
|
||||||
|
snprintf(name, 2048, "%s.imgz", basename);
|
||||||
|
out = fopen(name, "wb");
|
||||||
|
if (!out) {
|
||||||
|
fprintf(stderr, "Unable to save compressed file: %s\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fwrite(img, 4, 1, out);
|
||||||
|
fwrite(compressedData, compressedDataSize, 1, out);
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
// Experimental LZSA compression
|
||||||
|
const size_t srcSize2 = (sizeof(jlColorT) * 16) + (sizeof(jlPixelPairT) * 32000);
|
||||||
|
const int maxSize2 = lzsa_get_max_compressed_size_inmem(srcSize2);
|
||||||
|
unsigned char *compressedData2 = malloc((size_t)maxSize2);
|
||||||
|
if (!compressedData2) {
|
||||||
|
fprintf(stderr, "Unable to allocate space for compressed data.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int compressedDataSize2 = lzsa_compress_inmem((unsigned char *)img->palette, compressedData2, srcSize2, maxSize2, LZSA_FLAG_FAVOR_RATIO, 3, 1);
|
||||||
|
if (compressedDataSize2 <= 0) {
|
||||||
|
fprintf(stderr, "Unable to compress data.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(name, 2048, "%s.imgz2", basename);
|
||||||
|
out = fopen(name, "wb");
|
||||||
|
if (!out) {
|
||||||
|
fprintf(stderr, "Unable to save compressed file: %s\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fwrite(img, 4, 1, out);
|
||||||
|
fwrite(compressedData2, compressedDataSize2, 1, out);
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
if (showIt) {
|
if (showIt) {
|
||||||
jlImgDisplay(img);
|
jlImgDisplay(img);
|
||||||
|
|
|
@ -1,5 +1,26 @@
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
#
|
||||||
|
# JoeyLib
|
||||||
|
# Copyright (C) 2018-2021 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
#
|
||||||
|
# This software is provided 'as-is', without any express or implied
|
||||||
|
# warranty. In no event will the authors be held liable for any damages
|
||||||
|
# arising from the use of this software.
|
||||||
|
#
|
||||||
|
# Permission is granted to anyone to use this software for any purpose,
|
||||||
|
# including commercial applications, and to alter it and redistribute it
|
||||||
|
# freely, subject to the following restrictions:
|
||||||
|
#
|
||||||
|
# 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
# claim that you wrote the original software. If you use this software
|
||||||
|
# in a product, an acknowledgment in the product documentation would be
|
||||||
|
# appreciated but is not required.
|
||||||
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
# misrepresented as being the original software.
|
||||||
|
# 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
#
|
||||||
|
|
||||||
# Amiga
|
# Amiga
|
||||||
# https://github.com/bebbo/amiga-gcc
|
# https://github.com/bebbo/amiga-gcc
|
||||||
# m68k-amigaos-gcc -idirafter /home/scott/joey/sdks/amiga/m68k-amigaos/sys-include/machine -c ../src/joey.c
|
# m68k-amigaos-gcc -idirafter /home/scott/joey/sdks/amiga/m68k-amigaos/sys-include/machine -c ../src/joey.c
|
||||||
|
@ -75,22 +96,19 @@ function doIIgsBuild() {
|
||||||
iix assemble +L jIIgs.asm keep=31:/out/joey/jIIgsasm > jIIgs.asm.dis
|
iix assemble +L jIIgs.asm keep=31:/out/joey/jIIgsasm > jIIgs.asm.dis
|
||||||
iix compile jIIgs.c keep=31:/out/joey/jIIgsc
|
iix compile jIIgs.c keep=31:/out/joey/jIIgsc
|
||||||
iix compile joey.c keep=31:/out/joey/joey
|
iix compile joey.c keep=31:/out/joey/joey
|
||||||
|
|
||||||
iix makelib 31:/out/joey/joeylib +31:/out/joey/jIIgsasm.A
|
|
||||||
iix makelib 31:/out/joey/joeylib +31:/out/joey/jIIgsasm.ROOT
|
|
||||||
iix makelib 31:/out/joey/joeylib +31:/out/joey/jIIgsc.a
|
|
||||||
iix makelib 31:/out/joey/joeylib +31:/out/joey/joey.a
|
|
||||||
|
|
||||||
iix compile test.c keep=31:/out/joey/test
|
iix compile test.c keep=31:/out/joey/test
|
||||||
iix -DKeepType=S16 link +L 31:/out/joey/test 31:/out/joey/joeylib keep=31:/out/joey/test > test.map
|
iix -DKeepType=S16 link +L 31:/out/joey/jIIgsc 31:/out/joey/joey 31:/out/joey/jIIgsasm 31:/out/joey/test keep=31:/out/joey/test > test.map
|
||||||
|
|
||||||
iix dumpobj +D 31:/out/joey/test &> test.dis || true
|
iix dumpobj +D 31:/out/joey/test &> test.dis || true
|
||||||
|
|
||||||
php "${JOEY}/sdks/IIgs/ntpconverter/ntpconverter.php" *.mod
|
php "${JOEY}/sdks/IIgs/ntpconverter/ntpconverter.php" *.mod |& grep -v "PHP Notice" || true
|
||||||
popd
|
popd
|
||||||
|
|
||||||
cp -f "${OUT}/joeylib" "${DIST}/joeylib#b20000"
|
cp -f "${OUT}/jIIgsc.root" "${DIST}/jIIgsc.root#b10000"
|
||||||
cp -f "${JOEY}/sdks/IIgs/Tool035#ba0000" "${JOEY}/dist/IIgs/."
|
cp -f "${OUT}/jIIgsc.a" "${DIST}/jIIgsc.a#b10000"
|
||||||
|
cp -f "${OUT}/jIIgsasm.root" "${DIST}/jIIgsasm.root#b10000"
|
||||||
|
cp -f "${OUT}/jIIgsasm.a" "${DIST}/jIIgsasm.a#b10000"
|
||||||
|
cp -f "${OUT}/joey.a" "${DIST}/joey.a#b10000"
|
||||||
cp -f "${JOEY}/sdks/IIgs/Tool222#ba0000" "${JOEY}/dist/IIgs/."
|
cp -f "${JOEY}/sdks/IIgs/Tool222#ba0000" "${JOEY}/dist/IIgs/."
|
||||||
cp -f "${JOEY}/joeylib/scripts/build-IIgs.helper.sh" "${JOEY}/dist/."
|
cp -f "${JOEY}/joeylib/scripts/build-IIgs.helper.sh" "${JOEY}/dist/."
|
||||||
|
|
||||||
|
@ -135,15 +153,18 @@ function doIIgsBuild() {
|
||||||
|
|
||||||
function doPCBuild() {
|
function doPCBuild() {
|
||||||
|
|
||||||
|
#local G_CFLAGS="-Wall -D_REENTRANT_ -Dmain=SDL_main -I${SRC} -I${INSTALLED}/include -c"
|
||||||
local G_CFLAGS="-Wall -D_REENTRANT_ -I${SRC} -I${INSTALLED}/include -c"
|
local G_CFLAGS="-Wall -D_REENTRANT_ -I${SRC} -I${INSTALLED}/include -c"
|
||||||
|
|
||||||
echo "*** Starting ${DIST}"
|
echo "*** Starting ${DIST}"
|
||||||
createBuildAndDist
|
createBuildAndDist
|
||||||
|
|
||||||
pushd "${BUILD}"
|
pushd "${BUILD}"
|
||||||
|
"${CC}" ${CFLAGS} ${G_CFLAGS} -o jPixBuf.o "${SRC}/jPixBuf.c"
|
||||||
"${CC}" ${CFLAGS} ${G_CFLAGS} -o jSDL2.o "${SRC}/jSDL2.c"
|
"${CC}" ${CFLAGS} ${G_CFLAGS} -o jSDL2.o "${SRC}/jSDL2.c"
|
||||||
"${CC}" ${CFLAGS} ${G_CFLAGS} -o joey.o "${SRC}/joey.c"
|
"${CC}" ${CFLAGS} ${G_CFLAGS} -o joey.o "${SRC}/joey.c"
|
||||||
ar x "${INSTALLED}/lib/libSDL2.a"
|
ar x "${INSTALLED}/lib/libSDL2.a"
|
||||||
|
ar x "${INSTALLED}/lib/libSDL2main.a"
|
||||||
ar x "${INSTALLED}/lib/libSDL2_mixer.a"
|
ar x "${INSTALLED}/lib/libSDL2_mixer.a"
|
||||||
ar x "${INSTALLED}/lib/libmodplug.a"
|
ar x "${INSTALLED}/lib/libmodplug.a"
|
||||||
ar rcs "${DIST}/libjoeylib.a" *.o
|
ar rcs "${DIST}/libjoeylib.a" *.o
|
||||||
|
@ -164,10 +185,11 @@ TEST=$2
|
||||||
|
|
||||||
if [[ "${ARCH}x" == "x" ]]; then
|
if [[ "${ARCH}x" == "x" ]]; then
|
||||||
echo "$0 [arch | \"all\"]"
|
echo "$0 [arch | \"all\"]"
|
||||||
echo '(Where "arch" is amiga, iigs, linux32, linux64, macos32, macos64, st, windows32, or windows64.)'
|
echo '(Where "arch" is iigs, linux32, linux64, macosx32, macosx64, macosa64, win32, or win64.)'
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
:<<'SKIP'
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "amigax" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "amigax" ]]; then
|
||||||
DIST="${JOEY}/dist/amiga"
|
DIST="${JOEY}/dist/amiga"
|
||||||
|
|
||||||
|
@ -182,6 +204,7 @@ if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "amigax" ]]; then
|
||||||
|
|
||||||
#***TODO*** Emulator
|
#***TODO*** Emulator
|
||||||
fi
|
fi
|
||||||
|
SKIP
|
||||||
|
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "iigsx" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "iigsx" ]]; then
|
||||||
DIST="${JOEY}/dist/IIgs"
|
DIST="${JOEY}/dist/IIgs"
|
||||||
|
@ -206,7 +229,22 @@ if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "linux32x" ]]; then
|
||||||
doPCBuild
|
doPCBuild
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macos32x" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macosa64x" ]]; then
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_APPLE_DEPLOYMENT_TARGET}"
|
||||||
|
export MACOSX_DARWIN="${MACOSX_APPLE_DARWIN}"
|
||||||
|
export PATH="${MACOSX_APPLE_PATH}:${JOEYPATH}"
|
||||||
|
CC="oa64-clang"
|
||||||
|
CFLAGS=""
|
||||||
|
LDFLAGS=""
|
||||||
|
DIST="${JOEY}/dist/macOS/a64"
|
||||||
|
INSTALLED="${DEPS}/macOS/a64"
|
||||||
|
doPCBuild
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macosx32x" ]]; then
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_INTEL_DEPLOYMENT_TARGET}"
|
||||||
|
export MACOSX_DARWIN="${MACOSX_INTEL_DARWIN}"
|
||||||
|
export PATH="${MACOSX_INTEL_PATH}:${JOEYPATH}"
|
||||||
CC="o32-clang"
|
CC="o32-clang"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS=""
|
LDFLAGS=""
|
||||||
|
@ -215,7 +253,10 @@ if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macos32x" ]]; then
|
||||||
doPCBuild
|
doPCBuild
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macos64x" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macosx64x" ]]; then
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_INTEL_DEPLOYMENT_TARGET}"
|
||||||
|
export MACOSX_DARWIN="${MACOSX_INTEL_DARWIN}"
|
||||||
|
export PATH="${MACOSX_INTEL_PATH}:${JOEYPATH}"
|
||||||
CC="o64-clang"
|
CC="o64-clang"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS=""
|
LDFLAGS=""
|
||||||
|
@ -224,6 +265,7 @@ if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "macos64x" ]]; then
|
||||||
doPCBuild
|
doPCBuild
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
:<<'SKIP'
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "stx" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "stx" ]]; then
|
||||||
DIST="${JOEY}/dist/st"
|
DIST="${JOEY}/dist/st"
|
||||||
|
|
||||||
|
@ -242,20 +284,21 @@ if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "stx" ]]; then
|
||||||
#***TODO***
|
#***TODO***
|
||||||
#hatari -c "${JOEY}/sdks/st/hatari.cfg" -d out/
|
#hatari -c "${JOEY}/sdks/st/hatari.cfg" -d out/
|
||||||
fi
|
fi
|
||||||
|
SKIP
|
||||||
|
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "windows64x" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "win64x" ]]; then
|
||||||
CC="x86_64-w64-mingw32-gcc"
|
CC="x86_64-w64-mingw32-gcc"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS=""
|
LDFLAGS="-mwindows"
|
||||||
DIST="${JOEY}/dist/windows/x64"
|
DIST="${JOEY}/dist/windows/x64"
|
||||||
INSTALLED="${DEPS}/windows/x64"
|
INSTALLED="${DEPS}/windows/x64"
|
||||||
doPCBuild
|
doPCBuild
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "windows32x" ]]; then
|
if [[ "${ARCH}x" == "allx" || "${ARCH}x" == "win32x" ]]; then
|
||||||
CC="i686-w64-mingw32-gcc"
|
CC="i686-w64-mingw32-gcc"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS=""
|
LDFLAGS="-mwindows"
|
||||||
DIST="${JOEY}/dist/windows/i386"
|
DIST="${JOEY}/dist/windows/i386"
|
||||||
INSTALLED="${DEPS}/windows/i386"
|
INSTALLED="${DEPS}/windows/i386"
|
||||||
doPCBuild
|
doPCBuild
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
JOEY = /home/scott/joey
|
JOEY = /home/scott/joey
|
||||||
#CONFIG += SDL12
|
CONFIG += SDL12
|
||||||
CONFIG += SDL2
|
#CONFIG += SDL2
|
||||||
#CONFIG += dmalloc
|
#CONFIG += OURLIBS
|
||||||
|
|
||||||
|
#BREW = /home/linuxbrew/.linuxbrew
|
||||||
|
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
CONFIG += console
|
CONFIG += console
|
||||||
|
@ -10,28 +11,51 @@ CONFIG -= \
|
||||||
app_bundle \
|
app_bundle \
|
||||||
qt
|
qt
|
||||||
|
|
||||||
QMAKE_CFLAGS += \
|
DEFINES += JOEY_DEBUG
|
||||||
-I$$PWD/src \
|
|
||||||
-D_REENTRANT
|
# Determine which libraries to link against.
|
||||||
|
defined(BREW,var) {
|
||||||
|
# Use Homebrew libraries.
|
||||||
|
STATICLIB = $$BREW/lib
|
||||||
|
INCLUDEPATH += $$BREW/include
|
||||||
|
LIBS += \
|
||||||
|
-L$$STATICLIB \
|
||||||
|
-Wl,-rpath,$$STATICLIB
|
||||||
|
} else {
|
||||||
|
OURLIBS {
|
||||||
|
# Use our libraries.
|
||||||
|
STATICLIB = $$JOEY/sdks/linux/x64/lib
|
||||||
|
INCLUDEPATH += $$JOEY/sdks/linux/x64/include
|
||||||
|
LIBS += \
|
||||||
|
-L$$STATICLIB \
|
||||||
|
-Wl,-rpath,$$STATICLIB
|
||||||
|
} else {
|
||||||
|
# System libraries.
|
||||||
|
STATICLIB = /usr/lib/x86_64-linux-gnu
|
||||||
|
LIBS += \
|
||||||
|
-Wl,-rpath,$$STATICLIB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD/src
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
src/3rdparty/memwatch/memwatch.h \
|
||||||
|
src/jPixBuf.h \
|
||||||
src/joey.h \
|
src/joey.h \
|
||||||
src/stddclmr.h
|
src/stddclmr.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
src/3rdparty/memwatch/memwatch.c \
|
||||||
src/joey.c \
|
src/joey.c \
|
||||||
src/test.c
|
src/test.c
|
||||||
|
|
||||||
SDL12 {
|
SDL12 {
|
||||||
INCLUDEPATH += $$JOEY/sdks/linux/x64/include
|
SOURCES += \
|
||||||
|
src/jPixBuf.c \
|
||||||
SOURCES += src/jSDL12.c
|
src/jSDL12.c
|
||||||
|
|
||||||
LIBS += \
|
LIBS += \
|
||||||
-L$$JOEY/sdks/linux/x64/lib \
|
|
||||||
-Wl,-rpath,$$JOEY/sdks/linux/x64/lib \
|
|
||||||
-lSDL_mixer \
|
|
||||||
-lmikmod \
|
|
||||||
-lSDL \
|
-lSDL \
|
||||||
-lm \
|
-lm \
|
||||||
-ldl \
|
-ldl \
|
||||||
|
@ -39,35 +63,27 @@ SDL12 {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL2 {
|
SDL2 {
|
||||||
INCLUDEPATH += $$JOEY/sdks/linux/x64/include
|
QMAKE_CFLAGS += \
|
||||||
|
-Wall \
|
||||||
|
-D_REENTRANT
|
||||||
|
|
||||||
SOURCES += src/jSDL2.c
|
HEADERS += \
|
||||||
|
src/3rdparty/pocketmod/pocketmod.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
src/jPixBuf.c \
|
||||||
|
src/jSDL2.c
|
||||||
|
|
||||||
LIBS += \
|
LIBS += \
|
||||||
-L$$JOEY/sdks/linux/x64/lib \
|
|
||||||
-Wl,--enable-new-dtags \
|
-Wl,--enable-new-dtags \
|
||||||
-lSDL2 \
|
$$STATICLIB/libSDL2.a \
|
||||||
-lSDL2_mixer \
|
-lm -lasound -lm -ldl -lpthread -lpulse-simple -pthread -lpulse -pthread -lX11 -lXext -lXcursor -lXinerama -lXi -lXfixes -lXrandr -lXss -lXxf86vm -ldrm -lgbm -lwayland-egl -lwayland-client -lwayland-cursor -lxkbcommon -ldecor-0 -lpthread -lrt
|
||||||
-lmodplug \
|
|
||||||
-Wl,--no-undefined \
|
|
||||||
-lm \
|
|
||||||
-ldl \
|
|
||||||
-lpthread \
|
|
||||||
-lrt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OTHER_FILES += \
|
OTHER_FILES += \
|
||||||
src/jBlankcd ..c \
|
src/jBlank.c \
|
||||||
src/jAmiga.c \
|
src/jAmiga.c \
|
||||||
src/jIIgs.c \
|
src/jIIgs.c \
|
||||||
src/jIIgs.asm \
|
src/jIIgs.asm \
|
||||||
src/jIIgs.macro \
|
src/jIIgs.macro \
|
||||||
joey.pri
|
joey.pri
|
||||||
|
|
||||||
dmalloc {
|
|
||||||
DEFINES += DMALLOC
|
|
||||||
INCLUDEPATH += /opt/dmalloc/include
|
|
||||||
LIBS += \
|
|
||||||
-L/opt/dmalloc/lib \
|
|
||||||
-ldmallocth
|
|
||||||
}
|
|
||||||
|
|
133
joeylib/src/3rdparty/memwatch/FAQ
vendored
Normal file
133
joeylib/src/3rdparty/memwatch/FAQ
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
Frequently Asked Questions for memwatch
|
||||||
|
|
||||||
|
Q. I'm not getting any log file! What's wrong??
|
||||||
|
|
||||||
|
A. Did you define MEMWATCH when compiling all files?
|
||||||
|
Did you include memwatch.h in all the files?
|
||||||
|
If you did, then...:
|
||||||
|
|
||||||
|
Memwatch creates the file when it initializes. If you're not
|
||||||
|
getting the log file, it's because a) memwatch is not
|
||||||
|
initializing or b) it's initializing, but can't create the
|
||||||
|
file.
|
||||||
|
|
||||||
|
Memwatch has two functions, mwInit() and mwTerm(), that
|
||||||
|
initialize and terminate memwatch, respectively. They are
|
||||||
|
nestable. You USUALLY don't need to call mwInit() and
|
||||||
|
mwTerm(), since memwatch will auto-initialize on the first
|
||||||
|
call to a memory function, and then add mwTerm() to the
|
||||||
|
atexit() list.
|
||||||
|
|
||||||
|
You can call mwInit() and mwTerm() manually, if it's not
|
||||||
|
initializing properly or if your system doesn't support
|
||||||
|
atexit(). Call mwInit() as soon as you can, and mwTerm() at
|
||||||
|
the logical no-error ending of your program. Call mwAbort()
|
||||||
|
if the program is stopping due to an error; this will
|
||||||
|
terminate memwatch even if more than one call to mwTerm() is
|
||||||
|
outstanding.
|
||||||
|
|
||||||
|
If you are using C++, remember that global and static C++
|
||||||
|
objects constructors execute before main() when considering
|
||||||
|
where to put mwInit(). Also, their destructors execute after
|
||||||
|
main(). You may want to create a global object very early
|
||||||
|
with mwInit() in the constructor and mwTerm() in the
|
||||||
|
destructor. Too bad C++ does not guarantee initialization
|
||||||
|
order for global objects.
|
||||||
|
|
||||||
|
If this didn't help, try adding a call to mwDoFlush(1) after
|
||||||
|
mwInit(). If THAT didn't help, then memwatch is unable to
|
||||||
|
create the log file. Check write permissions.
|
||||||
|
|
||||||
|
If you can't use a log file, you can still use memwatch by
|
||||||
|
redirecting the output to a function of your choice. See the
|
||||||
|
next question.
|
||||||
|
|
||||||
|
Q. I'd like memwatch's output to pipe to my fave debugger! How?
|
||||||
|
|
||||||
|
A. Call mwSetOutFunc() with the address of a "void func(int c)"
|
||||||
|
function. You should also consider doing something about
|
||||||
|
the ARI handler, see memwatch.h for more details about that.
|
||||||
|
|
||||||
|
Q. Why isn't there any C++ support?
|
||||||
|
|
||||||
|
A. Because C++ is for sissies! =) Just kidding.
|
||||||
|
C++ comes with overridable allocation/deallocation
|
||||||
|
built-in. You can define your own new/delete operators
|
||||||
|
for any class, and thus circumvent memwatch, or confuse
|
||||||
|
it to no end. Also, the keywords "new" and "delete" may
|
||||||
|
appear in declarations in C++, making the preprocessor
|
||||||
|
replacement approach shaky. You can do it, but it's not
|
||||||
|
very stable.
|
||||||
|
|
||||||
|
If someone were to write a rock solid new/delete checker
|
||||||
|
for C++, there is no conflict with memwatch; use them both.
|
||||||
|
|
||||||
|
Q. I'm getting "WILD free" errors, but the code is bug-free!
|
||||||
|
|
||||||
|
A. If memwatch's free() recieves a pointer that wasn't allocated
|
||||||
|
by memwatch, a "WILD free" message appears. If the source of
|
||||||
|
the memory buffer is outside of memwatch (a non-standard
|
||||||
|
library function, for instance), you can use mwFree_() to
|
||||||
|
release it. mwFree_() calls free() on the pointer given if
|
||||||
|
memwatch can't recognize it, instead of blocking it.
|
||||||
|
|
||||||
|
Another source of "WILD free" messages is that if memwatch
|
||||||
|
is terminated before all memory allocated is freed, memwatch
|
||||||
|
will have forgotten about it, and thus generate the errors.
|
||||||
|
This is commonly caused by having memwatch auto-initialize,
|
||||||
|
and then using atexit() to clean up. When auto-initializing,
|
||||||
|
memwatch registers mwTerm() with atexit(), but if mwTerm()
|
||||||
|
runs before all memory is freed, then you will get "unfreed"
|
||||||
|
and "WILD free" messages when your own atexit()-registered
|
||||||
|
cleanup code runs, and frees the memory.
|
||||||
|
|
||||||
|
Q. I'm getting "unfreed" errors, but the code is bug-free!
|
||||||
|
|
||||||
|
A. You can get erroneous "unfreed" messages if memwatch
|
||||||
|
terminates before all memory has been freed. Try using
|
||||||
|
mwInit() and mwTerm() instead of auto-initialization.
|
||||||
|
|
||||||
|
If you _are_ using mwInit() and mwTerm(), it may be that
|
||||||
|
some code in your program executes before mwInit() or
|
||||||
|
after mwTerm(). Make sure that mwInit() is the first thing
|
||||||
|
executed, and mwTerm() the last.
|
||||||
|
|
||||||
|
Q. When compiling memwatch I get these 'might get clobbered'
|
||||||
|
errors, and something about a longjmp() inside memwatch.
|
||||||
|
|
||||||
|
A. When using gcc or egcs with the optimization to inline
|
||||||
|
functions, this warning occurs. This is because gcc and
|
||||||
|
egcs inlines memwatch's functions with setjmp/longjmp,
|
||||||
|
causing the calling functions to become unstable.
|
||||||
|
|
||||||
|
The gcc/egcs maintainers have been informed of this
|
||||||
|
problem, but until they modify the inline optimization
|
||||||
|
so that it leaves setjmp functions alone, make sure to
|
||||||
|
compile memwatch without inline function optimizations.
|
||||||
|
|
||||||
|
gcc 2.95.2 can be patched for this, and I have been told
|
||||||
|
it will be fixed in an upcoming version.
|
||||||
|
|
||||||
|
Q. My program crashes with SIGSEGV or alignment errors, but
|
||||||
|
only when I compile with memwatch enabled!
|
||||||
|
|
||||||
|
A. You are using a 64-bit (or higher) platform, and memwatch
|
||||||
|
was unable to detect and adjust for this. You'll have to
|
||||||
|
either compile with a suitable define for mwROUNDALLOC,
|
||||||
|
I suggest (number of bits / 8), or define mwROUNDALLOC
|
||||||
|
directly in memwatch.c.
|
||||||
|
|
||||||
|
Also, please check your limits.h file for the relevant
|
||||||
|
#defines, and tell me what they are.
|
||||||
|
|
||||||
|
Q. When I include string.h after memwatch.h, I get errors
|
||||||
|
related to strdup(), what gives?
|
||||||
|
|
||||||
|
A. Most, but probably not all, platforms are nice about
|
||||||
|
including files multiple times, so I could probably
|
||||||
|
avoid these errors by including string.h from memwatch.h.
|
||||||
|
But since I want to be on the safe side, I don't.
|
||||||
|
|
||||||
|
To fix this, simply include string.h before memwatch.h,
|
||||||
|
or modify memwatch.h to include string.h.
|
||||||
|
|
2
joeylib/src/3rdparty/memwatch/Makefile
vendored
Normal file
2
joeylib/src/3rdparty/memwatch/Makefile
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
test:
|
||||||
|
$(CC) -DMEMWATCH -DMW_STDIO test.c memwatch.c
|
99
joeylib/src/3rdparty/memwatch/README
vendored
Normal file
99
joeylib/src/3rdparty/memwatch/README
vendored
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
README for MEMWATCH 2.69
|
||||||
|
|
||||||
|
This file should be enough to get you started, and should be
|
||||||
|
enough for small projects. For more info, see the files USING
|
||||||
|
and the FAQ. If this is not enough, see memwatch.h, which is
|
||||||
|
well documented.
|
||||||
|
|
||||||
|
Memwatch is licensed under the GPL from version 2.69
|
||||||
|
onwards. Please read the file gpl.txt for more details.
|
||||||
|
|
||||||
|
If you choose to use memwatch to validate your projects, I
|
||||||
|
would like to hear about it. Please drop me a line at
|
||||||
|
johan@linkdata.se about the project itself, the hardware,
|
||||||
|
operating system, compiler and any URL(s) you feel is
|
||||||
|
appropriate.
|
||||||
|
|
||||||
|
***** To run the test program:
|
||||||
|
|
||||||
|
Look at the source code for test.c first. It does some really
|
||||||
|
nasty things, and I want you to be aware of that. If memwatch
|
||||||
|
can't capture SIGSEGV (General Protection Fault for Windoze),
|
||||||
|
your program will dump core (crash for Windoze).
|
||||||
|
|
||||||
|
Once you've done that, you can build the test program.
|
||||||
|
|
||||||
|
Linux and other *nixes with gcc:
|
||||||
|
|
||||||
|
gcc -o test -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c
|
||||||
|
|
||||||
|
Windows 95, Windows NT with MS Visual C++:
|
||||||
|
|
||||||
|
cl -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c
|
||||||
|
|
||||||
|
Then simply run the test program.
|
||||||
|
|
||||||
|
./test
|
||||||
|
|
||||||
|
|
||||||
|
***** Quick-start instructions:
|
||||||
|
|
||||||
|
1. Make sure that memwatch.h is included in all of the
|
||||||
|
source code files. If you have an include file that
|
||||||
|
all of the source code uses, you might be able to include
|
||||||
|
memwatch.h from there.
|
||||||
|
|
||||||
|
2. Recompile the program with MEMWATCH defined. See your
|
||||||
|
compiler's documentation if you don't know how to do this.
|
||||||
|
The usual switch looks like "-DMEMWATCH". To have MEMWATCH
|
||||||
|
use stderr for some output (like, "Abort, Retry, Ignore?"),
|
||||||
|
please also define MW_STDIO (or MEMWATCH_STDIO, same thing).
|
||||||
|
|
||||||
|
3. Run the program and examine the output in the
|
||||||
|
log file "memwatch.log". If you didn't get a log file,
|
||||||
|
you probably didn't do step 1 and 2 correctly, or your
|
||||||
|
program crashed before memwatch flushed the file buffer.
|
||||||
|
To have memwatch _always_ flush the buffer, add a call
|
||||||
|
to "mwDoFlush(1)" at the top of your main function.
|
||||||
|
|
||||||
|
4. There is no fourth step... but remember that there
|
||||||
|
are limits to what memwatch can do, and that you need
|
||||||
|
to be aware of them:
|
||||||
|
|
||||||
|
***** Limits to memwatch:
|
||||||
|
|
||||||
|
Memwatch cannot catch all wild pointer writes. It can catch
|
||||||
|
those it could make itself due to your program trashing
|
||||||
|
memwatch's internal data structures. It can catch, sort of,
|
||||||
|
wild writes into No Mans Land buffers (see the header file for
|
||||||
|
more info). Anything else and you're going to get core dumped,
|
||||||
|
or data corruption if you're lucky.
|
||||||
|
|
||||||
|
There are other limits of course, but that one is the most
|
||||||
|
serious one, and the one that you're likely to be suffering
|
||||||
|
from.
|
||||||
|
|
||||||
|
***** Can use memwatch with XXXXX?
|
||||||
|
|
||||||
|
Probably the answer is yes. It's been tested with several
|
||||||
|
different platforms and compilers. It may not work on yours
|
||||||
|
though... but there's only one way to find out.
|
||||||
|
|
||||||
|
***** Need more assistance?
|
||||||
|
|
||||||
|
I don't want e-mail on "how to program in C", or "I've got a
|
||||||
|
bug, help me". I _do_ want you to send email to me if you
|
||||||
|
find a bug in memwatch, or if it won't compile cleanly on your
|
||||||
|
system (assuming it's an ANSI-C compiler of course).
|
||||||
|
|
||||||
|
If you need help with using memwatch, read the header file.
|
||||||
|
If, after reading the header file, you still can't resolve the
|
||||||
|
problem, please mail me with the details.
|
||||||
|
|
||||||
|
I can be reached at "johan@linkdata.se".
|
||||||
|
|
||||||
|
The latest version of memwatch should be found at
|
||||||
|
"http://www.linkdata.se/".
|
||||||
|
|
||||||
|
Johan Lindh
|
||||||
|
|
213
joeylib/src/3rdparty/memwatch/USING
vendored
Normal file
213
joeylib/src/3rdparty/memwatch/USING
vendored
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
Using memwatch
|
||||||
|
==============
|
||||||
|
|
||||||
|
What is it?
|
||||||
|
|
||||||
|
Memwatch is primarily a memory leak detector for C. Besides
|
||||||
|
detecting leaks, it can do a bunch of other stuff, but lets
|
||||||
|
stay to the basics. If you _really_ want to know all the
|
||||||
|
gory details, you should check out the header file,
|
||||||
|
memwatch.h, and the source code. It's actually got some
|
||||||
|
comments! (Whoa, what a concept!)
|
||||||
|
|
||||||
|
How do I get the latest version?
|
||||||
|
|
||||||
|
http://www.linkdata.se/sourcecode.html
|
||||||
|
ftp://ftp.linkdata.se/pub/memwatch/
|
||||||
|
|
||||||
|
How does it work?
|
||||||
|
|
||||||
|
Using the C preprocessor, memwatch replaces all your
|
||||||
|
programs calls to ANSI C memory allocation functions with
|
||||||
|
calls to it's own functions, which keeps a record of all
|
||||||
|
allocations.
|
||||||
|
|
||||||
|
Memwatch is very unobtrusive; unless the define MEMWATCH is
|
||||||
|
defined, memwatch removes all traces of itself from the
|
||||||
|
code (using the preprocessor).
|
||||||
|
|
||||||
|
Memwatch normally writes it's data to the file
|
||||||
|
memwatch.log, but this can be overridden; see the section
|
||||||
|
on I/O, later.
|
||||||
|
|
||||||
|
Can I use it for my C++ sources?
|
||||||
|
|
||||||
|
You can, but it's not recommended. C++ allows individual
|
||||||
|
classes to have their own memory management, and the
|
||||||
|
preprocessor approach used by memwatch can cause havoc
|
||||||
|
with such class declarations if improperly used.
|
||||||
|
|
||||||
|
If you have no such classes, or have them but still want
|
||||||
|
to test it, you can give it a try.
|
||||||
|
|
||||||
|
First, re-enable the C++ support code in memwatch.
|
||||||
|
If you can't find it, you probably shouldn't be using
|
||||||
|
it. Then, in your source code, after including ALL
|
||||||
|
header files:
|
||||||
|
|
||||||
|
#define new mwNew
|
||||||
|
#define delete mwDelete
|
||||||
|
|
||||||
|
This will cause calls to new and delete in that source file
|
||||||
|
to be directed to memwatch. Also, be sure to read all the
|
||||||
|
text in memwatch.h regarding C++ support.
|
||||||
|
|
||||||
|
Is this stuff thread-safe?
|
||||||
|
|
||||||
|
I doubt it. As of version 2.66, there is rudimentary support
|
||||||
|
for threads, if you happen to be using Win32 or if you have
|
||||||
|
pthreads. Define WIN32 or MW_PTHREADS to signify this fact.
|
||||||
|
|
||||||
|
This will cause a global mutex to be created, and memwatch
|
||||||
|
will lock it when accessing the global memory chain, but it's
|
||||||
|
still far from certified threadsafe.
|
||||||
|
|
||||||
|
Initialization and cleanup
|
||||||
|
|
||||||
|
In order to do it's work in a timely fashion, memwatch
|
||||||
|
needs to do some startup and cleanup work. mwInit()
|
||||||
|
initializes memwatch and mwTerm() terminates it. Memwatch
|
||||||
|
can auto-initialize, and will do so if you don't call
|
||||||
|
mwInit() yourself. If this is the case, memwatch will use
|
||||||
|
atexit() to register mwTerm() to the atexit-queue.
|
||||||
|
|
||||||
|
The auto-init technique has a caveat; if you are using
|
||||||
|
atexit() yourself to do cleanup work, memwatch may
|
||||||
|
terminate before your program is done. To be on the safe
|
||||||
|
side, use mwInit() and mwTerm().
|
||||||
|
|
||||||
|
mwInit() and mwTerm() is nestable, so you can call mwInit()
|
||||||
|
several times, requiring mwTerm() to be called an equal
|
||||||
|
number of times to terminate memwatch.
|
||||||
|
|
||||||
|
In case of the program aborting in a controlled way, you
|
||||||
|
may want to call mwAbort() instead of mwTerm(). mwAbort()
|
||||||
|
will terminate memwatch even if there are outstanding calls
|
||||||
|
to mwTerm().
|
||||||
|
|
||||||
|
I/O operations
|
||||||
|
|
||||||
|
During normal operations, memwatch creates a file named
|
||||||
|
memwatch.log. Sometimes, memwatch.log can't be created;
|
||||||
|
then memwatch tries to create files name memwatNN.log,
|
||||||
|
where NN is between 01 and 99. If that fails, no log will
|
||||||
|
be produced.
|
||||||
|
|
||||||
|
If you can't use a file log, or don't want to, no worry.
|
||||||
|
Just call mwSetOutFunc() with the address of a "void
|
||||||
|
func(int c)" function, and all output will be directed
|
||||||
|
there, character by character.
|
||||||
|
|
||||||
|
Memwatch also has an Abort/Retry/Ignore handler that is
|
||||||
|
used when an ASSERT or VERIFY fails. The default handler
|
||||||
|
does no I/O, but automatically aborts the program. You can
|
||||||
|
use any handler you want; just send the address of a "int
|
||||||
|
func(const char*)" to mwSetAriFunc(). For more details on
|
||||||
|
that, see memwatch.h.
|
||||||
|
|
||||||
|
TRACE/ASSERT/VERIFY macros
|
||||||
|
|
||||||
|
Memwatch defines (if not already defined) the macros TRACE,
|
||||||
|
ASSERT and VERIFY. If you are already using macros with
|
||||||
|
these names, memwatch 2.61 and later will not override
|
||||||
|
them. Memwatch 2.61 and later will also always define the
|
||||||
|
macros mwTRACE, mwASSERT and mwVERIFY, so you can use these
|
||||||
|
to make sure you're talking to memwatch. Versions prior
|
||||||
|
to 2.61 will *OVERRIDE* existing TRACE, ASSERT and VERIFY.
|
||||||
|
|
||||||
|
To make sure that existing TRACE, ASSERT and VERIFY macros
|
||||||
|
are preserved, you can define MW_NOTRACE, MW_NOASSERT and
|
||||||
|
MW_NOVERIFY. All versions of memwatch will abide by these.
|
||||||
|
|
||||||
|
How slow can you go?
|
||||||
|
|
||||||
|
Memwatch slows things down. Large allocations aren't
|
||||||
|
affected so that you can measure it, but small allocations
|
||||||
|
that would utilize a compilers small-allocator function
|
||||||
|
suddenly cannot, and so gets slowed down alot. As a worst
|
||||||
|
case, expect it to be 3-5 times slower.
|
||||||
|
|
||||||
|
Free'ing gets hit worse, I'm afraid, as memwatch checks
|
||||||
|
a lot of stuff when freeing. Expect it to be 5-7 times
|
||||||
|
slower, no matter what the size of the allocation.
|
||||||
|
|
||||||
|
Stress-testing the application
|
||||||
|
|
||||||
|
You can simulate low-memory conditions using mwLimit().
|
||||||
|
mwLimit() takes the maximum number of bytes to be
|
||||||
|
allocated; when the limit is hit, allocation requests will
|
||||||
|
fail, and a "limit" message will be logged.
|
||||||
|
|
||||||
|
If you hit a real low-memory situation, memwatch logs that
|
||||||
|
too. Memwatch itself has some reserve memory tucked away so
|
||||||
|
it should continue running even in the worst conditions.
|
||||||
|
|
||||||
|
Hunting down wild writes and other Nasty Things
|
||||||
|
|
||||||
|
Wild writes are usually caused by using pointers that arent
|
||||||
|
initialized, or that were initialized, but then the memory
|
||||||
|
they points to is moved or freed. The best way to avoid
|
||||||
|
these kind of problems is to ALWAYS initialize pointers to
|
||||||
|
NULL, and after freeing a memory buffer, setting all
|
||||||
|
pointers that pointed to it to NULL.
|
||||||
|
|
||||||
|
To aid in tracking down uninitialized pointers memwatch
|
||||||
|
zaps all memory with certain values. Recently allocated
|
||||||
|
memory (unless calloc'd, of course), contains 0xFE.
|
||||||
|
Recently freed memory contains 0xFD. So if your program
|
||||||
|
crashes when using memwatch and not without memwatch, it's
|
||||||
|
most likely because you are not initializing your allocated
|
||||||
|
buffers, or using the buffers after they've been freed.
|
||||||
|
|
||||||
|
In the event that a wild pointer should damage memwatch's
|
||||||
|
internal data structures, memwatch employs checksums,
|
||||||
|
multiple copies of some values, and can also repair it's
|
||||||
|
own data structures.
|
||||||
|
|
||||||
|
If you are a paranoid person, and as programmer you should
|
||||||
|
be, you can use memwatch's mwIsReadAddr() and
|
||||||
|
mwIsSafeAddr() functions to check the accessibility of
|
||||||
|
memory. These are implemented for both ANSI C systems and
|
||||||
|
Win32 systems. Just put an mwASSERT() around the check and
|
||||||
|
forget about it.
|
||||||
|
|
||||||
|
Can I help?
|
||||||
|
|
||||||
|
Well, sure. For instance, I like memwatch to compile
|
||||||
|
without any warnings or errors. If you are using an ANSI C
|
||||||
|
compliant compiler, and are getting warnings or errors,
|
||||||
|
please mail me the details and instructions on how to fix
|
||||||
|
them, if you can.
|
||||||
|
|
||||||
|
Another thing you can do if you decide to use memwatch is
|
||||||
|
to mail me the name of the project(s) (and URL, if any),
|
||||||
|
hardware and operating system, compiler and what user
|
||||||
|
(organization). I will then post this info on the list of
|
||||||
|
memwatch users.
|
||||||
|
(http://www.linkdata.se/memwatchusers.html)
|
||||||
|
|
||||||
|
Top five problems using memwatch
|
||||||
|
|
||||||
|
5. Passed a non-memwatch allocated pointer to memwatch's
|
||||||
|
free(). Symtom: Causes an erroneous "WILD free" log
|
||||||
|
entry to appear. Cure: Either include memwatch.h for
|
||||||
|
the file that allocates, or use mwFree_() to free it.
|
||||||
|
|
||||||
|
4. Relied on auto-initialization when using atexit().
|
||||||
|
Symptom: Causes incorrect "unfreed" and "WILD free"
|
||||||
|
messages. Cure: Use mwInit() and mwTerm().
|
||||||
|
|
||||||
|
3. Forgot to include memwatch.h in all files. Symptom:
|
||||||
|
Tends to generate "WILD free" and "unfreed" messages.
|
||||||
|
Cure: Make sure to include memwatch.h!
|
||||||
|
|
||||||
|
2. No write permissions in currect directory. Symptom:
|
||||||
|
Seems like memwatch 'just aint working'. Cure: Use
|
||||||
|
mwSetOutFunc() to redirect output.
|
||||||
|
|
||||||
|
...and the number one problem is...
|
||||||
|
|
||||||
|
1. Didn't define MEMWATCH when compiling. Symptom:
|
||||||
|
Memwatch dutifully disables itself. Cure: Try adding
|
||||||
|
-DMEMWATCH to the command line.
|
||||||
|
|
340
joeylib/src/3rdparty/memwatch/gpl.txt
vendored
Normal file
340
joeylib/src/3rdparty/memwatch/gpl.txt
vendored
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Library General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Library General
|
||||||
|
Public License instead of this License.
|
2664
joeylib/src/3rdparty/memwatch/memwatch.c
vendored
Normal file
2664
joeylib/src/3rdparty/memwatch/memwatch.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
710
joeylib/src/3rdparty/memwatch/memwatch.h
vendored
Normal file
710
joeylib/src/3rdparty/memwatch/memwatch.h
vendored
Normal file
|
@ -0,0 +1,710 @@
|
||||||
|
/*
|
||||||
|
** MEMWATCH.H
|
||||||
|
** Nonintrusive ANSI C memory leak / overwrite detection
|
||||||
|
** Copyright (C) 1992-2002 Johan Lindh
|
||||||
|
** All rights reserved.
|
||||||
|
** Version 2.71
|
||||||
|
**
|
||||||
|
************************************************************************
|
||||||
|
**
|
||||||
|
** PURPOSE:
|
||||||
|
**
|
||||||
|
** MEMWATCH has been written to allow guys and gals that like to
|
||||||
|
** program in C a public-domain memory error control product.
|
||||||
|
** I hope you'll find it's as advanced as most commercial packages.
|
||||||
|
** The idea is that you use it during the development phase and
|
||||||
|
** then remove the MEMWATCH define to produce your final product.
|
||||||
|
** MEMWATCH is distributed in source code form in order to allow
|
||||||
|
** you to compile it for your platform with your own compiler.
|
||||||
|
** It's aim is to be 100% ANSI C, but some compilers are more stingy
|
||||||
|
** than others. If it doesn't compile without warnings, please mail
|
||||||
|
** me the configuration of operating system and compiler you are using
|
||||||
|
** along with a description of how to modify the source, and the version
|
||||||
|
** number of MEMWATCH that you are using.
|
||||||
|
**
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
This file is part of MEMWATCH.
|
||||||
|
|
||||||
|
MEMWATCH is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
MEMWATCH is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with MEMWATCH; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
**
|
||||||
|
** REVISION HISTORY:
|
||||||
|
**
|
||||||
|
** 920810 JLI [1.00]
|
||||||
|
** 920830 JLI [1.10 double-free detection]
|
||||||
|
** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit]
|
||||||
|
** 921022 JLI [1.20 ASSERT and VERIFY]
|
||||||
|
** 921105 JLI [1.30 C++ support and TRACE]
|
||||||
|
** 921116 JLI [1.40 mwSetOutFunc]
|
||||||
|
** 930215 JLI [1.50 modified ASSERT/VERIFY]
|
||||||
|
** 930327 JLI [1.51 better auto-init & PC-lint support]
|
||||||
|
** 930506 JLI [1.55 MemWatch class, improved C++ support]
|
||||||
|
** 930507 JLI [1.60 mwTest & CHECK()]
|
||||||
|
** 930809 JLI [1.65 Abort/Retry/Ignore]
|
||||||
|
** 930820 JLI [1.70 data dump when unfreed]
|
||||||
|
** 931016 JLI [1.72 modified C++ new/delete handling]
|
||||||
|
** 931108 JLI [1.77 mwSetAssertAction() & some small changes]
|
||||||
|
** 940110 JLI [1.80 no-mans-land alloc/checking]
|
||||||
|
** 940328 JLI [2.00 version 2.0 rewrite]
|
||||||
|
** Improved NML (no-mans-land) support.
|
||||||
|
** Improved performance (especially for free()ing!).
|
||||||
|
** Support for 'read-only' buffers (checksums)
|
||||||
|
** ^^ NOTE: I never did this... maybe I should?
|
||||||
|
** FBI (free'd block info) tagged before freed blocks
|
||||||
|
** Exporting of the mwCounter variable
|
||||||
|
** mwBreakOut() localizes debugger support
|
||||||
|
** Allocation statistics (global, per-module, per-line)
|
||||||
|
** Self-repair ability with relinking
|
||||||
|
** 950913 JLI [2.10 improved garbage handling]
|
||||||
|
** 951201 JLI [2.11 improved auto-free in emergencies]
|
||||||
|
** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()]
|
||||||
|
** 960514 JLI [2.12 undefining of existing macros]
|
||||||
|
** 960515 JLI [2.13 possibility to use default new() & delete()]
|
||||||
|
** 960516 JLI [2.20 suppression of file flushing on unfreed msgs]
|
||||||
|
** 960516 JLI [2.21 better support for using MEMWATCH with DLL's]
|
||||||
|
** 960710 JLI [X.02 multiple logs and mwFlushNow()]
|
||||||
|
** 960801 JLI [2.22 merged X.01 version with current]
|
||||||
|
** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
|
||||||
|
** 960805 JLI [2.31 merged X.02 version with current]
|
||||||
|
** 961002 JLI [2.32 support for realloc() + fixed STDERR bug]
|
||||||
|
** 961222 JLI [2.40 added mwMark() & mwUnmark()]
|
||||||
|
** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY]
|
||||||
|
** 970113 JLI [2.42 added support for PC-Lint 7.00g]
|
||||||
|
** 970207 JLI [2.43 added support for strdup()]
|
||||||
|
** 970209 JLI [2.44 changed default filename to lowercase]
|
||||||
|
** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers]
|
||||||
|
** 970723 JLI [2.46 added MW_ARI_NULLREAD flag]
|
||||||
|
** 970813 JLI [2.47 stabilized marker handling]
|
||||||
|
** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway]
|
||||||
|
** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support]
|
||||||
|
** 980417 JLI [2.51 more checks for invalid addresses]
|
||||||
|
** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting]
|
||||||
|
** 990112 JLI [2.53 added check for empty heap to mwIsOwned]
|
||||||
|
** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML]
|
||||||
|
** 990224 JLI [2.56 changed ordering of members in structures]
|
||||||
|
** 990303 JLI [2.57 first maybe-fixit-for-hpux test]
|
||||||
|
** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit]
|
||||||
|
** 990517 JLI [2.59 fixed some high-sensitivity warnings]
|
||||||
|
** 990610 JLI [2.60 fixed some more high-sensitivity warnings]
|
||||||
|
** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names]
|
||||||
|
** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()]
|
||||||
|
** 991007 JLI [2.63 first shot at a 64-bit compatible version]
|
||||||
|
** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const]
|
||||||
|
** 000704 JLI [2.65 added some more detection for 64-bits]
|
||||||
|
** 010502 JLI [2.66 incorporated some user fixes]
|
||||||
|
** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)]
|
||||||
|
** [added array destructor for C++ (thanks rdasilva@connecttel.com)]
|
||||||
|
** [added mutex support (thanks rdasilva@connecttel.com)]
|
||||||
|
** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined]
|
||||||
|
** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked]
|
||||||
|
** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen]
|
||||||
|
** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)]
|
||||||
|
** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)]
|
||||||
|
**
|
||||||
|
** To use, simply include 'MEMWATCH.H' as a header file,
|
||||||
|
** and add MEMWATCH.C to your list of files, and define the macro
|
||||||
|
** 'MEMWATCH'. If this is not defined, MEMWATCH will disable itself.
|
||||||
|
**
|
||||||
|
** To call the standard C malloc / realloc / calloc / free; use mwMalloc_(),
|
||||||
|
** mwCalloc_() and mwFree_(). Note that mwFree_() will correctly
|
||||||
|
** free both malloc()'d memory as well as mwMalloc()'d.
|
||||||
|
**
|
||||||
|
** 980317: C++ support has been disabled.
|
||||||
|
** The code remains, but is not compiled.
|
||||||
|
**
|
||||||
|
** For use with C++, which allows use of inlining in header files
|
||||||
|
** and class specific new/delete, you must also define 'new' as
|
||||||
|
** 'mwNew' and 'delete' as 'mwDelete'. Do this *after* you include
|
||||||
|
** C++ header files from libraries, otherwise you can mess up their
|
||||||
|
** class definitions. If you don't define these, the C++ allocations
|
||||||
|
** will not have source file and line number information. Also note,
|
||||||
|
** most C++ class libraries implement their own C++ memory management,
|
||||||
|
** and don't allow anyone to override them. MFC belongs to this crew.
|
||||||
|
** In these cases, the only thing to do is to use MEMWATCH_NOCPP.
|
||||||
|
**
|
||||||
|
** You can capture output from MEMWATCH using mwSetOutFunc().
|
||||||
|
** Just give it the adress of a "void myOutFunc(int c)" function,
|
||||||
|
** and all characters to be output will be redirected there.
|
||||||
|
**
|
||||||
|
** A failing ASSERT() or VERIFY() will normally always abort your
|
||||||
|
** program. This can be changed using mwSetAriFunc(). Give it a
|
||||||
|
** pointer to a "int myAriFunc(const char *)" function. Your function
|
||||||
|
** must ask the user whether to Abort, Retry or Ignore the trap.
|
||||||
|
** Return 2 to Abort, 1 to Retry or 0 to Ignore. Beware retry; it
|
||||||
|
** causes the expression to be evaluated again! MEMWATCH has a
|
||||||
|
** default ARI handler. It's disabled by default, but you can enable
|
||||||
|
** it by calling 'mwDefaultAri()'. Note that this will STILL abort
|
||||||
|
** your program unless you define MEMWATCH_STDIO to allow MEMWATCH
|
||||||
|
** to use the standard C I/O streams. Also, setting the ARI function
|
||||||
|
** will cause MEMWATCH *NOT* to write the ARI error to stderr. The
|
||||||
|
** error string is passed to the ARI function instead, as the
|
||||||
|
** 'const char *' parameter.
|
||||||
|
**
|
||||||
|
** You can disable MEMWATCH's ASSERT/VERIFY and/or TRACE implementations.
|
||||||
|
** This can be useful if you're using a debug terminal or smart debugger.
|
||||||
|
** Disable them by defining MW_NOASSERT, MW_NOVERIFY or MW_NOTRACE.
|
||||||
|
**
|
||||||
|
** MEMWATCH fills all allocated memory with the byte 0xFE, so if
|
||||||
|
** you're looking at erroneous data which are all 0xFE:s, the
|
||||||
|
** data probably was not initialized by you. The exception is
|
||||||
|
** calloc(), which will fill with zero's. All freed buffers are
|
||||||
|
** zapped with 0xFD. If this is what you look at, you're using
|
||||||
|
** data that has been freed. If this is the case, be aware that
|
||||||
|
** MEMWATCH places a 'free'd block info' structure immediately
|
||||||
|
** before the freed data. This block contains info about where
|
||||||
|
** the block was freed. The information is in readable text,
|
||||||
|
** in the format "FBI<counter>filename(line)", for example:
|
||||||
|
** "FBI<267>test.c(12)". Using FBI's slows down free(), so it's
|
||||||
|
** disabled by default. Use mwFreeBufferInfo(1) to enable it.
|
||||||
|
**
|
||||||
|
** To aid in tracking down wild pointer writes, MEMWATCH can perform
|
||||||
|
** no-mans-land allocations. No-mans-land will contain the byte 0xFC.
|
||||||
|
** MEMWATCH will, when this is enabled, convert recently free'd memory
|
||||||
|
** into NML allocations.
|
||||||
|
**
|
||||||
|
** MEMWATCH protects it's own data buffers with checksums. If you
|
||||||
|
** get an internal error, it means you're overwriting wildly,
|
||||||
|
** or using an uninitialized pointer.
|
||||||
|
**
|
||||||
|
************************************************************************
|
||||||
|
**
|
||||||
|
** Note when compiling with Microsoft C:
|
||||||
|
** - MSC ignores fflush() by default. This is overridden, so that
|
||||||
|
** the disk log will always be current.
|
||||||
|
**
|
||||||
|
** This utility has been tested with:
|
||||||
|
** PC-lint 7.0k, passed as 100% ANSI C compatible
|
||||||
|
** Microsoft Visual C++ on Win16 and Win32
|
||||||
|
** Microsoft C on DOS
|
||||||
|
** SAS C on an Amiga 500
|
||||||
|
** Gnu C on a PC running Red Hat Linux
|
||||||
|
** ...and using an (to me) unknown compiler on an Atari machine.
|
||||||
|
**
|
||||||
|
************************************************************************
|
||||||
|
**
|
||||||
|
** Format of error messages in MEMWATCH.LOG:
|
||||||
|
** message: <sequence-number> filename(linenumber), information
|
||||||
|
**
|
||||||
|
** Errors caught by MemWatch, when they are detected, and any
|
||||||
|
** actions taken besides writing to the log file MEMWATCH.LOG:
|
||||||
|
**
|
||||||
|
** Double-freeing:
|
||||||
|
** A pointer that was recently freed and has not since been
|
||||||
|
** reused was freed again. The place where the previous free()
|
||||||
|
** was executed is displayed.
|
||||||
|
** Detect: delete or free() using the offending pointer.
|
||||||
|
** Action: The delete or free() is cancelled, execution continues.
|
||||||
|
** Underflow:
|
||||||
|
** You have written just ahead of the allocated memory.
|
||||||
|
** The size and place of the allocation is displayed.
|
||||||
|
** Detect: delete or free() of the damaged buffer.
|
||||||
|
** Action: The buffer is freed, but there may be secondary damage.
|
||||||
|
** Overflow:
|
||||||
|
** Like underflow, but you've written after the end of the buffer.
|
||||||
|
** Detect: see Underflow.
|
||||||
|
** Action: see Underflow.
|
||||||
|
** WILD free:
|
||||||
|
** An unrecognized pointer was passed to delete or free().
|
||||||
|
** The pointer may have been returned from a library function;
|
||||||
|
** in that case, use mwFree_() to force free() of it.
|
||||||
|
** Also, this may be a double-free, but the previous free was
|
||||||
|
** too long ago, causing MEMWATCH to 'forget' it.
|
||||||
|
** Detect: delete or free() of the offending pointer.
|
||||||
|
** Action: The delete or free() is cancelled, execution continues.
|
||||||
|
** NULL free:
|
||||||
|
** It's unclear to me whether or not freeing of NULL pointers
|
||||||
|
** is legal in ANSI C, therefore a warning is written to the log file,
|
||||||
|
** but the error counter remains the same. This is legal using C++,
|
||||||
|
** so the warning does not appear with delete.
|
||||||
|
** Detect: When you free(NULL).
|
||||||
|
** Action: The free() is cancelled.
|
||||||
|
** Failed:
|
||||||
|
** A request to allocate memory failed. If the allocation is
|
||||||
|
** small, this may be due to memory depletion, but is more likely
|
||||||
|
** to be memory fragmentation problems. The amount of memory
|
||||||
|
** allocated so far is displayed also.
|
||||||
|
** Detect: When you new, malloc(), realloc() or calloc() memory.
|
||||||
|
** Action: NULL is returned.
|
||||||
|
** Realloc:
|
||||||
|
** A request to re-allocate a memory buffer failed for reasons
|
||||||
|
** other than out-of-memory. The specific reason is shown.
|
||||||
|
** Detect: When you realloc()
|
||||||
|
** Action: realloc() is cancelled, NULL is returned
|
||||||
|
** Limit fail:
|
||||||
|
** A request to allocate memory failed since it would violate
|
||||||
|
** the limit set using mwLimit(). mwLimit() is used to stress-test
|
||||||
|
** your code under simulated low memory conditions.
|
||||||
|
** Detect: At new, malloc(), realloc() or calloc().
|
||||||
|
** Action: NULL is returned.
|
||||||
|
** Assert trap:
|
||||||
|
** An ASSERT() failed. The ASSERT() macro works like C's assert()
|
||||||
|
** macro/function, except that it's interactive. See your C manual.
|
||||||
|
** Detect: On the ASSERT().
|
||||||
|
** Action: Program ends with an advisory message to stderr, OR
|
||||||
|
** Program writes the ASSERT to the log and continues, OR
|
||||||
|
** Program asks Abort/Retry/Ignore? and takes that action.
|
||||||
|
** Verify trap:
|
||||||
|
** A VERIFY() failed. The VERIFY() macro works like ASSERT(),
|
||||||
|
** but if MEMWATCH is not defined, it still evaluates the
|
||||||
|
** expression, but it does not act upon the result.
|
||||||
|
** Detect: On the VERIFY().
|
||||||
|
** Action: Program ends with an advisory message to stderr, OR
|
||||||
|
** Program writes the VERIFY to the log and continues, OR
|
||||||
|
** Program asks Abort/Retry/Ignore? and takes that action.
|
||||||
|
** Wild pointer:
|
||||||
|
** A no-mans-land buffer has been written into. MEMWATCH can
|
||||||
|
** allocate and distribute chunks of memory solely for the
|
||||||
|
** purpose of trying to catch random writes into memory.
|
||||||
|
** Detect: Always on CHECK(), but can be detected in several places.
|
||||||
|
** Action: The error is logged, and if an ARI handler is installed,
|
||||||
|
** it is executed, otherwise, execution continues.
|
||||||
|
** Unfreed:
|
||||||
|
** A memory buffer you allocated has not been freed.
|
||||||
|
** You are informed where it was allocated, and whether any
|
||||||
|
** over or underflow has occured. MemWatch also displays up to
|
||||||
|
** 16 bytes of the data, as much as it can, in hex and text.
|
||||||
|
** Detect: When MemWatch terminates.
|
||||||
|
** Action: The buffer is freed.
|
||||||
|
** Check:
|
||||||
|
** An error was detected during a CHECK() operation.
|
||||||
|
** The associated pointer is displayed along with
|
||||||
|
** the file and line where the CHECK() was executed.
|
||||||
|
** Followed immediately by a normal error message.
|
||||||
|
** Detect: When you CHECK()
|
||||||
|
** Action: Depends on the error
|
||||||
|
** Relink:
|
||||||
|
** After a MEMWATCH internal control block has been trashed,
|
||||||
|
** MEMWATCH tries to repair the damage. If successful, program
|
||||||
|
** execution will continue instead of aborting. Some information
|
||||||
|
** about the block may be gone permanently, though.
|
||||||
|
** Detect: N/A
|
||||||
|
** Action: Relink successful: program continues.
|
||||||
|
** Relink fails: program aborts.
|
||||||
|
** Internal:
|
||||||
|
** An internal error is flagged by MEMWATCH when it's control
|
||||||
|
** structures have been damaged. You are likely using an uninitialized
|
||||||
|
** pointer somewhere in your program, or are zapping memory all over.
|
||||||
|
** The message may give you additional diagnostic information.
|
||||||
|
** If possible, MEMWATCH will recover and continue execution.
|
||||||
|
** Detect: Various actions.
|
||||||
|
** Action: Whatever is needed
|
||||||
|
** Mark:
|
||||||
|
** The program terminated without umarking all marked pointers. Marking
|
||||||
|
** can be used to track resources other than memory. mwMark(pointer,text,...)
|
||||||
|
** when the resource is allocated, and mwUnmark(pointer) when it's freed.
|
||||||
|
** The 'text' is displayed for still marked pointers when the program
|
||||||
|
** ends.
|
||||||
|
** Detect: When MemWatch terminates.
|
||||||
|
** Action: The error is logged.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
************************************************************************
|
||||||
|
**
|
||||||
|
** The author may be reached by e-mail at the address below. If you
|
||||||
|
** mail me about source code changes in MEMWATCH, remember to include
|
||||||
|
** MW's version number.
|
||||||
|
**
|
||||||
|
** Johan Lindh
|
||||||
|
** johan@linkdata.se
|
||||||
|
**
|
||||||
|
** The latest version of MEMWATCH may be downloaded from
|
||||||
|
** http://www.linkdata.se/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MEMWATCH_H
|
||||||
|
#define __MEMWATCH_H
|
||||||
|
|
||||||
|
/* Make sure that malloc(), realloc(), calloc() and free() are declared. */
|
||||||
|
/*lint -save -e537 */
|
||||||
|
#include <stdlib.h>
|
||||||
|
/*lint -restore */
|
||||||
|
|
||||||
|
/* strdup() */
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Constants used
|
||||||
|
** All MEMWATCH constants start with the prefix MW_, followed by
|
||||||
|
** a short mnemonic which indicates where the constant is used,
|
||||||
|
** followed by a descriptive text about it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MW_ARI_NULLREAD 0x10 /* Null read (to start debugger) */
|
||||||
|
#define MW_ARI_ABORT 0x04 /* ARI handler says: abort program! */
|
||||||
|
#define MW_ARI_RETRY 0x02 /* ARI handler says: retry action! */
|
||||||
|
#define MW_ARI_IGNORE 0x01 /* ARI handler says: ignore error! */
|
||||||
|
|
||||||
|
#define MW_VAL_NEW 0xFE /* value in newly allocated memory */
|
||||||
|
#define MW_VAL_DEL 0xFD /* value in newly deleted memory */
|
||||||
|
#define MW_VAL_NML 0xFC /* value in no-mans-land */
|
||||||
|
#define MW_VAL_GRB 0xFB /* value in grabbed memory */
|
||||||
|
|
||||||
|
#define MW_TEST_ALL 0xFFFF /* perform all tests */
|
||||||
|
#define MW_TEST_CHAIN 0x0001 /* walk the heap chain */
|
||||||
|
#define MW_TEST_ALLOC 0x0002 /* test allocations & NML guards */
|
||||||
|
#define MW_TEST_NML 0x0004 /* test all-NML areas for modifications */
|
||||||
|
|
||||||
|
#define MW_NML_NONE 0 /* no NML */
|
||||||
|
#define MW_NML_FREE 1 /* turn FREE'd memory into NML */
|
||||||
|
#define MW_NML_ALL 2 /* all unused memory is NML */
|
||||||
|
#define MW_NML_DEFAULT 0 /* the default NML setting */
|
||||||
|
|
||||||
|
#define MW_STAT_GLOBAL 0 /* only global statistics collected */
|
||||||
|
#define MW_STAT_MODULE 1 /* collect statistics on a module basis */
|
||||||
|
#define MW_STAT_LINE 2 /* collect statistics on a line basis */
|
||||||
|
#define MW_STAT_DEFAULT 0 /* the default statistics setting */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** MemWatch internal constants
|
||||||
|
** You may change these and recompile MemWatch to change the limits
|
||||||
|
** of some parameters. Respect the recommended minimums!
|
||||||
|
*/
|
||||||
|
#define MW_TRACE_BUFFER 2048 /* (min 160) size of TRACE()'s output buffer */
|
||||||
|
#define MW_FREE_LIST 64 /* (min 4) number of free()'s to track */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Exported variables
|
||||||
|
** In case you have to remove the 'const' keyword because your compiler
|
||||||
|
** doesn't support it, be aware that changing the values may cause
|
||||||
|
** unpredictable behaviour.
|
||||||
|
** - mwCounter contains the current action count. You can use this to
|
||||||
|
** place breakpoints using a debugger, if you want.
|
||||||
|
*/
|
||||||
|
#ifndef __MEMWATCH_C
|
||||||
|
extern const unsigned long mwCounter;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** System functions
|
||||||
|
** Normally, it is not nessecary to call any of these. MEMWATCH will
|
||||||
|
** automatically initialize itself on the first MEMWATCH function call,
|
||||||
|
** and set up a call to mwAbort() using atexit(). Some C++ implementations
|
||||||
|
** run the atexit() chain before the program has terminated, so you
|
||||||
|
** may have to use mwInit() or the MemWatch C++ class to get good
|
||||||
|
** behaviour.
|
||||||
|
** - mwInit() can be called to disable the atexit() usage. If mwInit()
|
||||||
|
** is called directly, you must call mwTerm() to end MemWatch, or
|
||||||
|
** mwAbort().
|
||||||
|
** - mwTerm() is usually not nessecary to call; but if called, it will
|
||||||
|
** call mwAbort() if it finds that it is cancelling the 'topmost'
|
||||||
|
** mwInit() call.
|
||||||
|
** - mwAbort() cleans up after MEMWATCH, reports unfreed buffers, etc.
|
||||||
|
*/
|
||||||
|
void mwInit( void );
|
||||||
|
void mwTerm( void );
|
||||||
|
void mwAbort( void );
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Setup functions
|
||||||
|
** These functions control the operation of MEMWATCH's protective features.
|
||||||
|
** - mwFlushNow() causes MEMWATCH to flush it's buffers.
|
||||||
|
** - mwDoFlush() controls whether MEMWATCH flushes the disk buffers after
|
||||||
|
** writes. The default is smart flushing: MEMWATCH will not flush buffers
|
||||||
|
** explicitly until memory errors are detected. Then, all writes are
|
||||||
|
** flushed until program end or mwDoFlush(0) is called.
|
||||||
|
** - mwLimit() sets the allocation limit, an arbitrary limit on how much
|
||||||
|
** memory your program may allocate in bytes. Used to stress-test app.
|
||||||
|
** Also, in virtual-memory or multitasking environs, puts a limit on
|
||||||
|
** how much MW_NML_ALL can eat up.
|
||||||
|
** - mwGrab() grabs up X kilobytes of memory. Allocates actual memory,
|
||||||
|
** can be used to stress test app & OS both.
|
||||||
|
** - mwDrop() drops X kilobytes of grabbed memory.
|
||||||
|
** - mwNoMansLand() sets the behaviour of the NML logic. See the
|
||||||
|
** MW_NML_xxx for more information. The default is MW_NML_DEFAULT.
|
||||||
|
** - mwStatistics() sets the behaviour of the statistics collector. See
|
||||||
|
** the MW_STAT_xxx defines for more information. Default MW_STAT_DEFAULT.
|
||||||
|
** - mwFreeBufferInfo() enables or disables the tagging of free'd buffers
|
||||||
|
** with freeing information. This information is written in text form,
|
||||||
|
** using sprintf(), so it's pretty slow. Disabled by default.
|
||||||
|
** - mwAutoCheck() performs a CHECK() operation whenever a MemWatch function
|
||||||
|
** is used. Slows down performance, of course.
|
||||||
|
** - mwCalcCheck() calculates checksums for all data buffers. Slow!
|
||||||
|
** - mwDumpCheck() logs buffers where stored & calc'd checksums differ. Slow!!
|
||||||
|
** - mwMark() sets a generic marker. Returns the pointer given.
|
||||||
|
** - mwUnmark() removes a generic marker. If, at the end of execution, some
|
||||||
|
** markers are still in existence, these will be reported as leakage.
|
||||||
|
** returns the pointer given.
|
||||||
|
*/
|
||||||
|
void mwFlushNow( void );
|
||||||
|
void mwDoFlush( int onoff );
|
||||||
|
void mwLimit( long bytes );
|
||||||
|
unsigned mwGrab( unsigned kilobytes );
|
||||||
|
unsigned mwDrop( unsigned kilobytes );
|
||||||
|
void mwNoMansLand( int mw_nml_level );
|
||||||
|
void mwStatistics( int level );
|
||||||
|
void mwFreeBufferInfo( int onoff );
|
||||||
|
void mwAutoCheck( int onoff );
|
||||||
|
void mwCalcCheck( void );
|
||||||
|
void mwDumpCheck( void );
|
||||||
|
void * mwMark( void *p, const char *description, const char *file, unsigned line );
|
||||||
|
void * mwUnmark( void *p, const char *file, unsigned line );
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Testing/verification/tracing
|
||||||
|
** All of these macros except VERIFY() evaluates to a null statement
|
||||||
|
** if MEMWATCH is not defined during compilation.
|
||||||
|
** - mwIsReadAddr() checks a memory area for read privilige.
|
||||||
|
** - mwIsSafeAddr() checks a memory area for both read & write privilige.
|
||||||
|
** This function and mwIsReadAddr() is highly system-specific and
|
||||||
|
** may not be implemented. If this is the case, they will default
|
||||||
|
** to returning nonzero for any non-NULL pointer.
|
||||||
|
** - CHECK() does a complete memory integrity test. Slow!
|
||||||
|
** - CHECK_THIS() checks only selected components.
|
||||||
|
** - CHECK_BUFFER() checks the indicated buffer for errors.
|
||||||
|
** - mwASSERT() or ASSERT() If the expression evaluates to nonzero, execution continues.
|
||||||
|
** Otherwise, the ARI handler is called, if present. If not present,
|
||||||
|
** the default ARI action is taken (set with mwSetAriAction()).
|
||||||
|
** ASSERT() can be disabled by defining MW_NOASSERT.
|
||||||
|
** - mwVERIFY() or VERIFY() works just like ASSERT(), but when compiling without
|
||||||
|
** MEMWATCH the macro evaluates to the expression.
|
||||||
|
** VERIFY() can be disabled by defining MW_NOVERIFY.
|
||||||
|
** - mwTRACE() or TRACE() writes some text and data to the log. Use like printf().
|
||||||
|
** TRACE() can be disabled by defining MW_NOTRACE.
|
||||||
|
*/
|
||||||
|
int mwIsReadAddr( const void *p, unsigned len );
|
||||||
|
int mwIsSafeAddr( void *p, unsigned len );
|
||||||
|
int mwTest( const char *file, int line, int mw_test_flags );
|
||||||
|
int mwTestBuffer( const char *file, int line, void *p );
|
||||||
|
int mwAssert( int, const char*, const char*, int );
|
||||||
|
int mwVerify( int, const char*, const char*, int );
|
||||||
|
|
||||||
|
/*
|
||||||
|
** User I/O functions
|
||||||
|
** - mwTrace() works like printf(), but dumps output either to the
|
||||||
|
** function specified with mwSetOutFunc(), or the log file.
|
||||||
|
** - mwPuts() works like puts(), dumps output like mwTrace().
|
||||||
|
** - mwSetOutFunc() allows you to give the adress of a function
|
||||||
|
** where all user output will go. (exeption: see mwSetAriFunc)
|
||||||
|
** Specifying NULL will direct output to the log file.
|
||||||
|
** - mwSetAriFunc() gives MEMWATCH the adress of a function to call
|
||||||
|
** when an 'Abort, Retry, Ignore' question is called for. The
|
||||||
|
** actual error message is NOT printed when you've set this adress,
|
||||||
|
** but instead it is passed as an argument. If you call with NULL
|
||||||
|
** for an argument, the ARI handler is disabled again. When the
|
||||||
|
** handler is disabled, MEMWATCH will automatically take the
|
||||||
|
** action specified by mwSetAriAction().
|
||||||
|
** - mwSetAriAction() sets the default ARI return value MEMWATCH should
|
||||||
|
** use if no ARI handler is specified. Defaults to MW_ARI_ABORT.
|
||||||
|
** - mwAriHandler() is an ANSI ARI handler you can use if you like. It
|
||||||
|
** dumps output to stderr, and expects input from stdin.
|
||||||
|
** - mwBreakOut() is called in certain cases when MEMWATCH feels it would
|
||||||
|
** be nice to break into a debugger. If you feel like MEMWATCH, place
|
||||||
|
** an execution breakpoint on this function.
|
||||||
|
*/
|
||||||
|
void mwTrace( const char* format_string, ... );
|
||||||
|
void mwPuts( const char* text );
|
||||||
|
void mwSetOutFunc( void (*func)(int) );
|
||||||
|
void mwSetAriFunc( int (*func)(const char*) );
|
||||||
|
void mwSetAriAction( int mw_ari_value );
|
||||||
|
int mwAriHandler( const char* cause );
|
||||||
|
void mwBreakOut( const char* cause );
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Allocation/deallocation functions
|
||||||
|
** These functions are the ones actually to perform allocations
|
||||||
|
** when running MEMWATCH, for both C and C++ calls.
|
||||||
|
** - mwMalloc() debugging allocator
|
||||||
|
** - mwMalloc_() always resolves to a clean call of malloc()
|
||||||
|
** - mwRealloc() debugging re-allocator
|
||||||
|
** - mwRealloc_() always resolves to a clean call of realloc()
|
||||||
|
** - mwCalloc() debugging allocator, fills with zeros
|
||||||
|
** - mwCalloc_() always resolves to a clean call of calloc()
|
||||||
|
** - mwFree() debugging free. Can only free memory which has
|
||||||
|
** been allocated by MEMWATCH.
|
||||||
|
** - mwFree_() resolves to a) normal free() or b) debugging free.
|
||||||
|
** Can free memory allocated by MEMWATCH and malloc() both.
|
||||||
|
** Does not generate any runtime errors.
|
||||||
|
*/
|
||||||
|
void* mwMalloc( size_t, const char*, int );
|
||||||
|
void* mwMalloc_( size_t );
|
||||||
|
void* mwRealloc( void *, size_t, const char*, int );
|
||||||
|
void* mwRealloc_( void *, size_t );
|
||||||
|
void* mwCalloc( size_t, size_t, const char*, int );
|
||||||
|
void* mwCalloc_( size_t, size_t );
|
||||||
|
void mwFree( void*, const char*, int );
|
||||||
|
void mwFree_( void* );
|
||||||
|
char* mwStrdup( const char *, const char*, int );
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Enable/disable precompiler block
|
||||||
|
** This block of defines and if(n)defs make sure that references
|
||||||
|
** to MEMWATCH is completely removed from the code if the MEMWATCH
|
||||||
|
** manifest constant is not defined.
|
||||||
|
*/
|
||||||
|
#ifndef __MEMWATCH_C
|
||||||
|
#ifdef MEMWATCH
|
||||||
|
|
||||||
|
#define mwASSERT(exp) while(mwAssert((int)(exp),#exp,__FILE__,__LINE__))
|
||||||
|
#ifndef MW_NOASSERT
|
||||||
|
#ifndef ASSERT
|
||||||
|
#define ASSERT mwASSERT
|
||||||
|
#endif /* !ASSERT */
|
||||||
|
#endif /* !MW_NOASSERT */
|
||||||
|
#define mwVERIFY(exp) while(mwVerify((int)(exp),#exp,__FILE__,__LINE__))
|
||||||
|
#ifndef MW_NOVERIFY
|
||||||
|
#ifndef VERIFY
|
||||||
|
#define VERIFY mwVERIFY
|
||||||
|
#endif /* !VERIFY */
|
||||||
|
#endif /* !MW_NOVERIFY */
|
||||||
|
#define mwTRACE mwTrace
|
||||||
|
#ifndef MW_NOTRACE
|
||||||
|
#ifndef TRACE
|
||||||
|
#define TRACE mwTRACE
|
||||||
|
#endif /* !TRACE */
|
||||||
|
#endif /* !MW_NOTRACE */
|
||||||
|
|
||||||
|
/* some compilers use a define and not a function */
|
||||||
|
/* for strdup(). */
|
||||||
|
#ifdef strdup
|
||||||
|
#undef strdup
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define malloc(n) mwMalloc(n,__FILE__,__LINE__)
|
||||||
|
#define strdup(p) mwStrdup(p,__FILE__,__LINE__)
|
||||||
|
#define realloc(p,n) mwRealloc(p,n,__FILE__,__LINE__)
|
||||||
|
#define calloc(n,m) mwCalloc(n,m,__FILE__,__LINE__)
|
||||||
|
#define free(p) mwFree(p,__FILE__,__LINE__)
|
||||||
|
#define CHECK() mwTest(__FILE__,__LINE__,MW_TEST_ALL)
|
||||||
|
#define CHECK_THIS(n) mwTest(__FILE__,__LINE__,n)
|
||||||
|
#define CHECK_BUFFER(b) mwTestBuffer(__FILE__,__LINE__,b)
|
||||||
|
#define MARK(p) mwMark(p,#p,__FILE__,__LINE__)
|
||||||
|
#define UNMARK(p) mwUnmark(p,__FILE__,__LINE__)
|
||||||
|
|
||||||
|
#else /* MEMWATCH */
|
||||||
|
|
||||||
|
#define mwASSERT(exp)
|
||||||
|
#ifndef MW_NOASSERT
|
||||||
|
#ifndef ASSERT
|
||||||
|
#define ASSERT mwASSERT
|
||||||
|
#endif /* !ASSERT */
|
||||||
|
#endif /* !MW_NOASSERT */
|
||||||
|
|
||||||
|
#define mwVERIFY(exp) exp
|
||||||
|
#ifndef MW_NOVERIFY
|
||||||
|
#ifndef VERIFY
|
||||||
|
#define VERIFY mwVERIFY
|
||||||
|
#endif /* !VERIFY */
|
||||||
|
#endif /* !MW_NOVERIFY */
|
||||||
|
|
||||||
|
/*lint -esym(773,mwTRACE) */
|
||||||
|
#define mwTRACE /*lint -save -e506 */ 1?(void)0:mwDummyTraceFunction /*lint -restore */
|
||||||
|
#ifndef MW_NOTRACE
|
||||||
|
#ifndef TRACE
|
||||||
|
/*lint -esym(773,TRACE) */
|
||||||
|
#define TRACE mwTRACE
|
||||||
|
#endif /* !TRACE */
|
||||||
|
#endif /* !MW_NOTRACE */
|
||||||
|
|
||||||
|
extern void mwDummyTraceFunction(const char *,...);
|
||||||
|
/*lint -save -e652 */
|
||||||
|
#define mwDoFlush(n)
|
||||||
|
#define mwPuts(s)
|
||||||
|
#define mwInit()
|
||||||
|
#define mwGrab(n)
|
||||||
|
#define mwDrop(n)
|
||||||
|
#define mwLimit(n)
|
||||||
|
#define mwTest(f,l)
|
||||||
|
#define mwSetOutFunc(f)
|
||||||
|
#define mwSetAriFunc(f)
|
||||||
|
#define mwDefaultAri()
|
||||||
|
#define mwNomansland()
|
||||||
|
#define mwStatistics(f)
|
||||||
|
#define mwMark(p,t,f,n) (p)
|
||||||
|
#define mwUnmark(p,f,n) (p)
|
||||||
|
#define mwMalloc(n,f,l) malloc(n)
|
||||||
|
#define mwStrdup(p,f,l) strdup(p)
|
||||||
|
#define mwRealloc(p,n,f,l) realloc(p,n)
|
||||||
|
#define mwCalloc(n,m,f,l) calloc(n,m)
|
||||||
|
#define mwFree(p) free(p)
|
||||||
|
#define mwMalloc_(n) malloc(n)
|
||||||
|
#define mwRealloc_(p,n) realloc(p,n)
|
||||||
|
#define mwCalloc_(n,m) calloc(n,m)
|
||||||
|
#define mwFree_(p) free(p)
|
||||||
|
#define mwAssert(e,es,f,l)
|
||||||
|
#define mwVerify(e,es,f,l) (e)
|
||||||
|
#define mwTrace mwDummyTrace
|
||||||
|
#define mwTestBuffer(f,l,b) (0)
|
||||||
|
#define CHECK()
|
||||||
|
#define CHECK_THIS(n)
|
||||||
|
#define CHECK_BUFFER(b)
|
||||||
|
#define MARK(p) (p)
|
||||||
|
#define UNMARK(p) (p)
|
||||||
|
/*lint -restore */
|
||||||
|
|
||||||
|
#endif /* MEMWATCH */
|
||||||
|
#endif /* !__MEMWATCH_C */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 /* 980317: disabled C++ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** C++ support section
|
||||||
|
** Implements the C++ support. Please note that in order to avoid
|
||||||
|
** messing up library classes, C++ support is disabled by default.
|
||||||
|
** You must NOT enable it until AFTER the inclusion of all header
|
||||||
|
** files belonging to code that are not compiled with MEMWATCH, and
|
||||||
|
** possibly for some that are! The reason for this is that a C++
|
||||||
|
** class may implement it's own new() function, and the preprocessor
|
||||||
|
** would substitute this crucial declaration for MEMWATCH new().
|
||||||
|
** You can forcibly deny C++ support by defining MEMWATCH_NOCPP.
|
||||||
|
** To enble C++ support, you must be compiling C++, MEMWATCH must
|
||||||
|
** be defined, MEMWATCH_NOCPP must not be defined, and finally,
|
||||||
|
** you must define 'new' to be 'mwNew', and 'delete' to be 'mwDelete'.
|
||||||
|
** Unlike C, C++ code can begin executing *way* before main(), for
|
||||||
|
** example if a global variable is created. For this reason, you can
|
||||||
|
** declare a global variable of the class 'MemWatch'. If this is
|
||||||
|
** is the first variable created, it will then check ALL C++ allocations
|
||||||
|
** and deallocations. Unfortunately, this evaluation order is not
|
||||||
|
** guaranteed by C++, though the compilers I've tried evaluates them
|
||||||
|
** in the order encountered.
|
||||||
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#ifndef __MEMWATCH_C
|
||||||
|
#ifdef MEMWATCH
|
||||||
|
#ifndef MEMWATCH_NOCPP
|
||||||
|
extern int mwNCur;
|
||||||
|
extern const char *mwNFile;
|
||||||
|
extern int mwNLine;
|
||||||
|
class MemWatch {
|
||||||
|
public:
|
||||||
|
MemWatch();
|
||||||
|
~MemWatch();
|
||||||
|
};
|
||||||
|
void * operator new(size_t);
|
||||||
|
void * operator new(size_t,const char *,int);
|
||||||
|
void * operator new[] (size_t,const char *,int); // hjc 07/16/02
|
||||||
|
void operator delete(void *);
|
||||||
|
#define mwNew new(__FILE__,__LINE__)
|
||||||
|
#define mwDelete (mwNCur=1,mwNFile=__FILE__,mwNLine=__LINE__),delete
|
||||||
|
#endif /* MEMWATCH_NOCPP */
|
||||||
|
#endif /* MEMWATCH */
|
||||||
|
#endif /* !__MEMWATCH_C */
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* 980317: disabled C++ */
|
||||||
|
|
||||||
|
#endif /* __MEMWATCH_H */
|
||||||
|
|
||||||
|
/* EOF MEMWATCH.H */
|
15
joeylib/src/3rdparty/memwatch/memwatch.lsm
vendored
Normal file
15
joeylib/src/3rdparty/memwatch/memwatch.lsm
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Begin3
|
||||||
|
Title: memwatch
|
||||||
|
Version: 2.71
|
||||||
|
Entered-date: 2002-09-18
|
||||||
|
Description: fault tolerant ANSI-C source code memory leak and corruption detection
|
||||||
|
Keywords: memwatch debugging library memory leak source code ansi c
|
||||||
|
Author: johan@linkdata.se
|
||||||
|
Maintained-by: johan@linkdata.se
|
||||||
|
Primary-site: ftp.linkdata.se /pub/memwatch
|
||||||
|
42K memwatch-2.71.tar.gz
|
||||||
|
Alternate-site:
|
||||||
|
Original-site: ftp.linkdata.se /pub/memwatch
|
||||||
|
Platforms: all
|
||||||
|
Copying-policy: GPL
|
||||||
|
End
|
116
joeylib/src/3rdparty/memwatch/test.C
vendored
Normal file
116
joeylib/src/3rdparty/memwatch/test.C
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
** NOTE: Running this program in a Win32 or Unix environment
|
||||||
|
** will probably result in a segmentation fault or protection
|
||||||
|
** error. These errors may be caused by MEMWATCH when it is
|
||||||
|
** looking at memory to see if it owns it, or may be caused by
|
||||||
|
** the test program writing to memory it does not own.
|
||||||
|
**
|
||||||
|
** MEMWATCH has two functions called 'mwIsReadAddr()' and
|
||||||
|
** 'mwIsSafeAddr()', which are system-specific.
|
||||||
|
** If they are implemented for your system, and works
|
||||||
|
** correctly, MEMWATCH will identify garbage pointers and
|
||||||
|
** avoid causing segmentation faults, GP's etc.
|
||||||
|
**
|
||||||
|
** If they are NOT implemented, count on getting the core
|
||||||
|
** dumped when running this test program! As of this writing,
|
||||||
|
** the safe-address checking has been implemented for Win32
|
||||||
|
** and ANSI-C compliant systems. The ANSI-C checking traps
|
||||||
|
** SIGSEGV and uses setjmp/longjmp to resume processing.
|
||||||
|
**
|
||||||
|
** Note for Win95 users: The Win32 IsBadReadPtr() and its
|
||||||
|
** similar functions can return incorrect values. This has
|
||||||
|
** not happened under WinNT, though, just Win95.
|
||||||
|
**
|
||||||
|
** 991009 Johan Lindh
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "memwatch.h"
|
||||||
|
|
||||||
|
#ifndef SIGSEGV
|
||||||
|
#error "SIGNAL.H does not define SIGSEGV; running this program WILL cause a core dump/crash!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MEMWATCH
|
||||||
|
#error "You really, really don't want to run this without memwatch. Trust me."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MW_STDIO) && !defined(MEMWATCH_STDIO)
|
||||||
|
#error "Define MW_STDIO and try again, please."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
/* Collect stats on a line number basis */
|
||||||
|
mwStatistics( 2 );
|
||||||
|
|
||||||
|
/* Slows things down, but OK for this test prg */
|
||||||
|
/* mwAutoCheck( 1 ); */
|
||||||
|
|
||||||
|
TRACE("Hello world!\n");
|
||||||
|
|
||||||
|
p = malloc(210);
|
||||||
|
free(p);
|
||||||
|
p = malloc(20);
|
||||||
|
p = malloc(200); /* causes unfreed error */
|
||||||
|
p[-1] = 0; /* causes underflow error */
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
p = malloc(100);
|
||||||
|
p[ -(int)(sizeof(long)*8) ] = -1; /* try to damage MW's heap chain */
|
||||||
|
free( p ); /* should cause relink */
|
||||||
|
|
||||||
|
mwSetAriFunc( mwAriHandler );
|
||||||
|
ASSERT(1==2);
|
||||||
|
|
||||||
|
mwLimit(1000000);
|
||||||
|
mwNoMansLand( MW_NML_ALL );
|
||||||
|
|
||||||
|
/* These may cause a general protection fault (segmentation fault) */
|
||||||
|
/* They're here to help test the no-mans-land protection */
|
||||||
|
if( mwIsSafeAddr(p+50000,1) ) {
|
||||||
|
TRACE("Killing byte at %p\n", p+50000);
|
||||||
|
*(p+50000) = 0;
|
||||||
|
}
|
||||||
|
if( mwIsSafeAddr(p+30000,1) ) {
|
||||||
|
TRACE("Killing byte at %p\n", p+30000);
|
||||||
|
*(p+30000) = 0;
|
||||||
|
}
|
||||||
|
if( mwIsSafeAddr(p+1000,1) ) {
|
||||||
|
TRACE("Killing byte at %p\n", p+1000);
|
||||||
|
*(p+1000) = 0;
|
||||||
|
}
|
||||||
|
if( mwIsSafeAddr(p-100,1) ) {
|
||||||
|
TRACE("Killing byte at %p\n", p-100);
|
||||||
|
*(p-100) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This may cause a GP fault as well, since MW data buffers */
|
||||||
|
/* have been damaged in the above killing spree */
|
||||||
|
CHECK();
|
||||||
|
|
||||||
|
p = malloc(12000);
|
||||||
|
p[-5] = 1;
|
||||||
|
p[-10] = 2;
|
||||||
|
p[-15] = 3;
|
||||||
|
p[-20] = 4;
|
||||||
|
|
||||||
|
/* This may cause a GP fault since MW's buffer list may have */
|
||||||
|
/* been damaged by above killing, and it will try to repair it. */
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
p = realloc(p,10); /* causes realloc: free'd from error */
|
||||||
|
|
||||||
|
/* May cause GP since MW will inspect the memory to see if it owns it. */
|
||||||
|
free( (void*)main );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Comment out the following line to compile. */
|
||||||
|
#error "Hey! Don't just compile this program, read the comments first!"
|
195
joeylib/src/3rdparty/pocketmod/README.md
vendored
Normal file
195
joeylib/src/3rdparty/pocketmod/README.md
vendored
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
# About #
|
||||||
|
|
||||||
|
pocketmod is a small ANSI C library for turning ProTracker [MOD files][1] into
|
||||||
|
playable PCM audio. It comes as an STB-style [single-file library][2], with no
|
||||||
|
external dependencies (not even the C standard library). The library has been
|
||||||
|
tested on a wide range of MOD files, and should be fairly accurate. The code is
|
||||||
|
MIT-licensed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Using the library #
|
||||||
|
|
||||||
|
## Integration ##
|
||||||
|
|
||||||
|
`pocketmod.h` is meant to be dropped into your source tree and compiled along
|
||||||
|
with the rest of your code. To create the library implementation, #define
|
||||||
|
`POCKETMOD_IMPLEMENTATION` before including the header in one source file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Library API ##
|
||||||
|
|
||||||
|
This is the entire public API of the library:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct pocketmod_context pocketmod_context;
|
||||||
|
int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate);
|
||||||
|
int pocketmod_render(pocketmod_context *c, void *buffer, int size);
|
||||||
|
int pocketmod_loop_count(pocketmod_context *c);
|
||||||
|
```
|
||||||
|
|
||||||
|
Below is a detailed description of each part.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### pocketmod_context ###
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct pocketmod_context {
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This structure holds state used during rendering. It's declared publicly so that
|
||||||
|
you can instantiate it anywhere you like, but its data fields should be
|
||||||
|
considered private. The structure is relatively small (less than 2 KiB), so you
|
||||||
|
can put it on the stack if you want.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### pocketmod_init ###
|
||||||
|
|
||||||
|
```c
|
||||||
|
int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function initializes a context and prepares it for rendering from the start
|
||||||
|
of a song. `data` is a buffer containing MOD file data, and `size` is the size
|
||||||
|
of that buffer in bytes. `rate` is the desired sample rate in Hertz, for example
|
||||||
|
44100 for CD-quality audio. The function returns a nonzero value on success, or
|
||||||
|
zero if it was given an invalid MOD file.
|
||||||
|
|
||||||
|
Note that the library reads pattern and instrument data directly out of `data`
|
||||||
|
during rendering, so you should make sure that it remains valid until the last
|
||||||
|
call to `pocketmod_render()`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### pocketmod_render ###
|
||||||
|
|
||||||
|
```c
|
||||||
|
int pocketmod_render(pocketmod_context *c, void *buffer, int size);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function renders the next part of the song and writes the rendered PCM
|
||||||
|
audio data to `buffer`, which should be at least `size` bytes large. It returns
|
||||||
|
the number of bytes worth of sample data that was written to `buffer`.
|
||||||
|
|
||||||
|
Each sample consists of two `float` PCM values, first the left channel and then
|
||||||
|
the right channel. `pocketmod_render()` generally writes as many samples as it
|
||||||
|
can, but it may stop short when reaching the end of a pattern. This is done so
|
||||||
|
that you can check if the song looped, and decide for yourself if you want to
|
||||||
|
keep going or not.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### pocketmod_loop_count ###
|
||||||
|
|
||||||
|
```c
|
||||||
|
int pocketmod_loop_count(pocketmod_context *c);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function returns the number of times the song has looped. The song is
|
||||||
|
considered to have looped when reaching a previously encountered pattern order
|
||||||
|
position.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Configuration #
|
||||||
|
|
||||||
|
There are a few preprocessor symbols that may be #defined before including
|
||||||
|
`pocketmod.h` to change the behavior of the library:
|
||||||
|
|
||||||
|
- `POCKETMOD_MAX_CHANNELS`: The maximum number of channels. The default value is
|
||||||
|
32 (the highest number for any MOD format). Lowering this value reduces the
|
||||||
|
size of the `pocketmod_context` structure. The downside is that it will make
|
||||||
|
`pocketmod_init()` fail for any songs that need more channels. This setting is
|
||||||
|
useful if, for example, you know that all of your songs only use four
|
||||||
|
channels.
|
||||||
|
- `POCKETMOD_MAX_SAMPLES`: Same as the above, but for samples (instruments). The
|
||||||
|
default value is 31 (the highest number for any MOD format).
|
||||||
|
- `POCKETMOD_NO_INTERPOLATION`: You can define this to disable sample
|
||||||
|
interpolation. This gives the rendered audio a sharper, rougher sound, which
|
||||||
|
is sometimes preferred for chiptunes.
|
||||||
|
|
||||||
|
Note that if you define any of the above symbols, you should make sure that
|
||||||
|
they're defined the same in *every* source file that includes `pocketmod.h`. I
|
||||||
|
recommend that you use your build system to do this consistently across your
|
||||||
|
whole project.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Example programs #
|
||||||
|
|
||||||
|
There are two small example programs included, to demostrate how the library is
|
||||||
|
used.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## MOD to WAV converter ##
|
||||||
|
|
||||||
|
This is a simple command-line tool for converting MOD files to WAV files. Here's
|
||||||
|
an example of what building and using it looks like:
|
||||||
|
|
||||||
|
$ make converter
|
||||||
|
cc examples/converter.c -o converter -I.
|
||||||
|
$ ./converter songs/spacedeb.mod spacedeb.wav
|
||||||
|
Writing: 'spacedeb.wav' [54.0 MB] [5:05] Press Ctrl + C to stop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## SDL2-based MOD player ##
|
||||||
|
|
||||||
|
This is a command-line MOD player. To build the example, you need to have
|
||||||
|
[SDL2][3] installed. On Debian-based Linux you can get it the usual way:
|
||||||
|
|
||||||
|
$ sudo apt-get install libsdl2-dev
|
||||||
|
|
||||||
|
And here's what it looks like:
|
||||||
|
|
||||||
|
$ make player
|
||||||
|
cc examples/player.c -o player -I. -lSDL2main -lSDL2
|
||||||
|
$ ./player songs/king.mod
|
||||||
|
Playing 'king.mod' [00:03] Press Ctrl + C to stop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Song credits #
|
||||||
|
|
||||||
|
A small collection of test songs is included in the `songs/` directory. All
|
||||||
|
credit for these go to their respective authors. I have shortened the filename
|
||||||
|
of some of the songs, but they are otherwise unaltered.
|
||||||
|
|
||||||
|
| Filename | Author | License |
|
||||||
|
|----------------------|------------------|-------------------------|
|
||||||
|
| [bananasplit.mod][6] | dizzy / cncd | [ModArchive license][4] |
|
||||||
|
| [chill.mod][7] | Chromag | [ModArchive license][4] |
|
||||||
|
| [elysium.mod][8] | Jester | [CC BY-NC-SA 3.0][5] |
|
||||||
|
| [king.mod][9] | Dreamweaver | [ModArchive license][4] |
|
||||||
|
| [nemesis.mod][10] | radix | [ModArchive license][4] |
|
||||||
|
| [overture.mod][11] | Jogeir Liljedahl | [ModArchive license][4] |
|
||||||
|
| [spacedeb.mod][12] | Markus Kaarlonen | [ModArchive license][4] |
|
||||||
|
| [stardstm.mod][13] | Jester | [CC BY-NC-SA 3.0][5] |
|
||||||
|
| [sundance.mod][14] | Purple Motion | [ModArchive license][4] |
|
||||||
|
| [sundown.mod][15] | Chromag | [ModArchive license][4] |
|
||||||
|
| [supernova.mod][16] | radix | [ModArchive license][4] |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1]: https://en.wikipedia.org/wiki/MOD_(file_format)
|
||||||
|
[2]: https://github.com/nothings/single_file_libs
|
||||||
|
[3]: https://libsdl.org/
|
||||||
|
[4]: https://modarchive.org/index.php?terms-upload
|
||||||
|
[5]: https://creativecommons.org/licenses/by-nc-sa/3.0/
|
||||||
|
[6]: https://modarchive.org/module.php?35151
|
||||||
|
[7]: https://modarchive.org/module.php?85693
|
||||||
|
[8]: https://modarchive.org/module.php?40475
|
||||||
|
[9]: https://modarchive.org/module.php?93821
|
||||||
|
[10]: https://modarchive.org/module.php?164417
|
||||||
|
[11]: https://modarchive.org/module.php?51549
|
||||||
|
[12]: https://modarchive.org/module.php?57925
|
||||||
|
[13]: https://modarchive.org/module.php?59344
|
||||||
|
[14]: https://modarchive.org/module.php?171453
|
||||||
|
[15]: https://modarchive.org/module.php?159847
|
||||||
|
[16]: https://modarchive.org/module.php?164025
|
871
joeylib/src/3rdparty/pocketmod/pocketmod.h
vendored
Normal file
871
joeylib/src/3rdparty/pocketmod/pocketmod.h
vendored
Normal file
|
@ -0,0 +1,871 @@
|
||||||
|
/* See end of file for license */
|
||||||
|
|
||||||
|
#ifndef POCKETMOD_H_INCLUDED
|
||||||
|
#define POCKETMOD_H_INCLUDED
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct pocketmod_context pocketmod_context;
|
||||||
|
int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate);
|
||||||
|
int pocketmod_render(pocketmod_context *c, void *buffer, int size);
|
||||||
|
int pocketmod_loop_count(pocketmod_context *c);
|
||||||
|
|
||||||
|
#ifndef POCKETMOD_MAX_CHANNELS
|
||||||
|
#define POCKETMOD_MAX_CHANNELS 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef POCKETMOD_MAX_SAMPLES
|
||||||
|
#define POCKETMOD_MAX_SAMPLES 31
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
signed char *data; /* Sample data buffer */
|
||||||
|
unsigned int length; /* Data length (in bytes) */
|
||||||
|
} _pocketmod_sample;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char dirty; /* Pitch/volume dirty flags */
|
||||||
|
unsigned char sample; /* Sample number (0..31) */
|
||||||
|
unsigned char volume; /* Base volume without tremolo (0..64) */
|
||||||
|
unsigned char balance; /* Stereo balance (0..255) */
|
||||||
|
unsigned short period; /* Note period (113..856) */
|
||||||
|
unsigned short delayed; /* Delayed note period (113..856) */
|
||||||
|
unsigned short target; /* Target period (for tone portamento) */
|
||||||
|
unsigned char finetune; /* Note finetune (0..15) */
|
||||||
|
unsigned char loop_count; /* E6x loop counter */
|
||||||
|
unsigned char loop_line; /* E6x target line */
|
||||||
|
unsigned char lfo_step; /* Vibrato/tremolo LFO step counter */
|
||||||
|
unsigned char lfo_type[2]; /* LFO type for vibrato/tremolo */
|
||||||
|
unsigned char effect; /* Current effect (0x0..0xf or 0xe0..0xef) */
|
||||||
|
unsigned char param; /* Raw effect parameter value */
|
||||||
|
unsigned char param3; /* Parameter memory for 3xx */
|
||||||
|
unsigned char param4; /* Parameter memory for 4xy */
|
||||||
|
unsigned char param7; /* Parameter memory for 7xy */
|
||||||
|
unsigned char param9; /* Parameter memory for 9xx */
|
||||||
|
unsigned char paramE1; /* Parameter memory for E1x */
|
||||||
|
unsigned char paramE2; /* Parameter memory for E2x */
|
||||||
|
unsigned char paramEA; /* Parameter memory for EAx */
|
||||||
|
unsigned char paramEB; /* Parameter memory for EBx */
|
||||||
|
unsigned char real_volume; /* Volume (with tremolo adjustment) */
|
||||||
|
float position; /* Position in sample data buffer */
|
||||||
|
float increment; /* Position increment per output sample */
|
||||||
|
} _pocketmod_chan;
|
||||||
|
|
||||||
|
struct pocketmod_context
|
||||||
|
{
|
||||||
|
/* Read-only song data */
|
||||||
|
_pocketmod_sample samples[POCKETMOD_MAX_SAMPLES];
|
||||||
|
unsigned char *source; /* Pointer to source MOD data */
|
||||||
|
unsigned char *order; /* Pattern order table */
|
||||||
|
unsigned char *patterns; /* Start of pattern data */
|
||||||
|
unsigned char length; /* Patterns in the order (1..128) */
|
||||||
|
unsigned char reset; /* Pattern to loop back to (0..127) */
|
||||||
|
unsigned char num_patterns; /* Patterns in the file (1..128) */
|
||||||
|
unsigned char num_samples; /* Sample count (15 or 31) */
|
||||||
|
unsigned char num_channels; /* Channel count (1..32) */
|
||||||
|
|
||||||
|
/* Timing variables */
|
||||||
|
int samples_per_second; /* Sample rate (set by user) */
|
||||||
|
int ticks_per_line; /* A.K.A. song speed (initially 6) */
|
||||||
|
float samples_per_tick; /* Depends on sample rate and BPM */
|
||||||
|
|
||||||
|
/* Loop detection state */
|
||||||
|
unsigned char visited[16]; /* Bit mask of previously visited patterns */
|
||||||
|
int loop_count; /* How many times the song has looped */
|
||||||
|
|
||||||
|
/* Render state */
|
||||||
|
_pocketmod_chan channels[POCKETMOD_MAX_CHANNELS];
|
||||||
|
unsigned char pattern_delay;/* EEx pattern delay counter */
|
||||||
|
unsigned int lfo_rng; /* RNG used for the random LFO waveform */
|
||||||
|
|
||||||
|
/* Position in song (from least to most granular) */
|
||||||
|
signed char pattern; /* Current pattern in order */
|
||||||
|
signed char line; /* Current line in pattern */
|
||||||
|
short tick; /* Current tick in line */
|
||||||
|
float sample; /* Current sample in tick */
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef POCKETMOD_IMPLEMENTATION
|
||||||
|
|
||||||
|
/* Memorize a parameter unless the new value is zero */
|
||||||
|
#define POCKETMOD_MEM(dst, src) do { \
|
||||||
|
(dst) = (src) ? (src) : (dst); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Same thing, but memorize each nibble separately */
|
||||||
|
#define POCKETMOD_MEM2(dst, src) do { \
|
||||||
|
(dst) = (((src) & 0x0f) ? ((src) & 0x0f) : ((dst) & 0x0f)) \
|
||||||
|
| (((src) & 0xf0) ? ((src) & 0xf0) : ((dst) & 0xf0)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Shortcut to sample metadata (sample must be nonzero) */
|
||||||
|
#define POCKETMOD_SAMPLE(c, sample) ((c)->source + 12 + 30 * (sample))
|
||||||
|
|
||||||
|
/* Channel dirty flags */
|
||||||
|
#define POCKETMOD_PITCH 0x01
|
||||||
|
#define POCKETMOD_VOLUME 0x02
|
||||||
|
|
||||||
|
/* The size of one sample in bytes */
|
||||||
|
#define POCKETMOD_SAMPLE_SIZE sizeof(float[2])
|
||||||
|
|
||||||
|
/* Finetune adjustment table. Three octaves for each finetune setting. */
|
||||||
|
static const signed char _pocketmod_finetune[16][36] = {
|
||||||
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{ -6, -6, -5, -5, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -3, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0},
|
||||||
|
{-12,-12,-10,-11, -8, -8, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -4, -4, -4, -3, -3, -3, -3, -2, -3, -3, -2, -3, -3, -2, -2, -2, -2, -2, -2, -1},
|
||||||
|
{-18,-17,-16,-16,-13,-12,-12,-11,-10,-10,-10, -9, -9, -9, -8, -8, -7, -6, -6, -5, -5, -5, -5, -4, -5, -4, -3, -4, -4, -3, -3, -3, -3, -2, -2, -2},
|
||||||
|
{-24,-23,-21,-21,-18,-17,-16,-15,-14,-13,-13,-12,-12,-12,-11,-10, -9, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3},
|
||||||
|
{-30,-29,-26,-26,-23,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-13,-11,-11,-10, -9, -9, -9, -8, -7, -8, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4},
|
||||||
|
{-36,-34,-32,-31,-27,-26,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-11,-10,-10, -9, -9, -9, -7, -8, -7, -6, -6, -6, -6, -5, -5, -4},
|
||||||
|
{-42,-40,-37,-36,-32,-30,-29,-27,-25,-24,-23,-22,-21,-20,-18,-18,-16,-15,-14,-13,-13,-12,-12,-10,-10,-10, -9, -9, -9, -8, -7, -7, -7, -6, -6, -5},
|
||||||
|
{ 51, 48, 46, 42, 42, 38, 36, 34, 32, 30, 24, 27, 25, 24, 23, 21, 21, 19, 18, 17, 16, 15, 14, 14, 12, 12, 12, 10, 10, 10, 9, 8, 8, 8, 7, 7},
|
||||||
|
{ 44, 42, 40, 37, 37, 35, 32, 31, 29, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 15, 14, 13, 12, 11, 10, 10, 9, 9, 9, 8, 7, 7, 7, 6, 6},
|
||||||
|
{ 38, 36, 34, 32, 31, 30, 28, 27, 25, 24, 22, 21, 19, 18, 17, 16, 16, 15, 14, 13, 13, 12, 11, 11, 9, 9, 9, 8, 7, 7, 7, 6, 6, 6, 5, 5},
|
||||||
|
{ 31, 30, 29, 26, 26, 25, 24, 22, 21, 20, 18, 17, 16, 15, 14, 13, 13, 12, 12, 11, 11, 10, 9, 9, 8, 7, 8, 7, 6, 6, 6, 5, 5, 5, 5, 5},
|
||||||
|
{ 25, 24, 23, 21, 21, 20, 19, 18, 17, 16, 14, 14, 13, 12, 11, 10, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 3, 4},
|
||||||
|
{ 19, 18, 17, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 9, 9, 8, 8, 18, 7, 7, 7, 6, 5, 6, 5, 4, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3},
|
||||||
|
{ 12, 12, 12, 10, 11, 11, 10, 10, 9, 8, 7, 7, 6, 6, 6, 5, 6, 5, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2},
|
||||||
|
{ 6, 6, 6, 5, 6, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Min/max helper functions */
|
||||||
|
static int _pocketmod_min(int x, int y) { return x < y ? x : y; }
|
||||||
|
static int _pocketmod_max(int x, int y) { return x > y ? x : y; }
|
||||||
|
|
||||||
|
/* Clamp a volume value to the 0..64 range */
|
||||||
|
static int _pocketmod_clamp_volume(int x)
|
||||||
|
{
|
||||||
|
x = _pocketmod_max(x, 0x00);
|
||||||
|
x = _pocketmod_min(x, 0x40);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero out a block of memory */
|
||||||
|
static void _pocketmod_zero(void *data, int size)
|
||||||
|
{
|
||||||
|
char *byte = (char *)data, *end = byte + size;
|
||||||
|
while (byte != end) { *byte++ = 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a period (at finetune = 0) to a note index in 0..35 */
|
||||||
|
static int _pocketmod_period_to_note(int period)
|
||||||
|
{
|
||||||
|
switch (period) {
|
||||||
|
case 856: return 0; case 808: return 1; case 762: return 2;
|
||||||
|
case 720: return 3; case 678: return 4; case 640: return 5;
|
||||||
|
case 604: return 6; case 570: return 7; case 538: return 8;
|
||||||
|
case 508: return 9; case 480: return 10; case 453: return 11;
|
||||||
|
case 428: return 12; case 404: return 13; case 381: return 14;
|
||||||
|
case 360: return 15; case 339: return 16; case 320: return 17;
|
||||||
|
case 302: return 18; case 285: return 19; case 269: return 20;
|
||||||
|
case 254: return 21; case 240: return 22; case 226: return 23;
|
||||||
|
case 214: return 24; case 202: return 25; case 190: return 26;
|
||||||
|
case 180: return 27; case 170: return 28; case 160: return 29;
|
||||||
|
case 151: return 30; case 143: return 31; case 135: return 32;
|
||||||
|
case 127: return 33; case 120: return 34; case 113: return 35;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table-based sine wave oscillator */
|
||||||
|
static int _pocketmod_sin(int step)
|
||||||
|
{
|
||||||
|
/* round(sin(x * pi / 32) * 255) for x in 0..15 */
|
||||||
|
static const unsigned char sin[16] = {
|
||||||
|
0x00, 0x19, 0x32, 0x4a, 0x62, 0x78, 0x8e, 0xa2,
|
||||||
|
0xb4, 0xc5, 0xd4, 0xe0, 0xec, 0xf4, 0xfa, 0xfe
|
||||||
|
};
|
||||||
|
int x = sin[step & 0x0f];
|
||||||
|
x = (step & 0x1f) < 0x10 ? x : 0xff - x;
|
||||||
|
return step < 0x20 ? x : -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Oscillators for vibrato/tremolo effects */
|
||||||
|
static int _pocketmod_lfo(pocketmod_context *c, _pocketmod_chan *ch, int step)
|
||||||
|
{
|
||||||
|
switch (ch->lfo_type[ch->effect == 7] & 3) {
|
||||||
|
case 0: return _pocketmod_sin(step & 0x3f); /* Sine */
|
||||||
|
case 1: return 0xff - ((step & 0x3f) << 3); /* Saw */
|
||||||
|
case 2: return (step & 0x3f) < 0x20 ? 0xff : -0xff; /* Square */
|
||||||
|
case 3: return (c->lfo_rng & 0x1ff) - 0xff; /* Random */
|
||||||
|
default: return 0; /* Hush little compiler */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_update_pitch(pocketmod_context *c, _pocketmod_chan *ch)
|
||||||
|
{
|
||||||
|
/* Don't do anything if the period is zero */
|
||||||
|
ch->increment = 0.0f;
|
||||||
|
if (ch->period) {
|
||||||
|
float period = ch->period;
|
||||||
|
|
||||||
|
/* Apply vibrato (if active) */
|
||||||
|
if (ch->effect == 0x4 || ch->effect == 0x6) {
|
||||||
|
int step = (ch->param4 >> 4) * ch->lfo_step;
|
||||||
|
int rate = ch->param4 & 0x0f;
|
||||||
|
period += _pocketmod_lfo(c, ch, step) * rate / 128.0f;
|
||||||
|
|
||||||
|
/* Apply arpeggio (if active) */
|
||||||
|
} else if (ch->effect == 0x0 && ch->param) {
|
||||||
|
static const float arpeggio[16] = { /* 2^(X/12) for X in 0..15 */
|
||||||
|
1.000000f, 1.059463f, 1.122462f, 1.189207f,
|
||||||
|
1.259921f, 1.334840f, 1.414214f, 1.498307f,
|
||||||
|
1.587401f, 1.681793f, 1.781797f, 1.887749f,
|
||||||
|
2.000000f, 2.118926f, 2.244924f, 2.378414f
|
||||||
|
};
|
||||||
|
int step = (ch->param >> ((2 - c->tick % 3) << 2)) & 0x0f;
|
||||||
|
period /= arpeggio[step];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate sample buffer position increment */
|
||||||
|
ch->increment = 3546894.6f / (period * c->samples_per_second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the pitch dirty flag */
|
||||||
|
ch->dirty &= ~POCKETMOD_PITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_update_volume(pocketmod_context *c, _pocketmod_chan *ch)
|
||||||
|
{
|
||||||
|
int volume = ch->volume;
|
||||||
|
if (ch->effect == 0x7) {
|
||||||
|
int step = ch->lfo_step * (ch->param7 >> 4);
|
||||||
|
volume += _pocketmod_lfo(c, ch, step) * (ch->param7 & 0x0f) >> 6;
|
||||||
|
}
|
||||||
|
ch->real_volume = _pocketmod_clamp_volume(volume);
|
||||||
|
ch->dirty &= ~POCKETMOD_VOLUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_pitch_slide(_pocketmod_chan *ch, int amount)
|
||||||
|
{
|
||||||
|
int max = 856 + _pocketmod_finetune[ch->finetune][ 0];
|
||||||
|
int min = 113 + _pocketmod_finetune[ch->finetune][35];
|
||||||
|
ch->period += amount;
|
||||||
|
ch->period = _pocketmod_max(ch->period, min);
|
||||||
|
ch->period = _pocketmod_min(ch->period, max);
|
||||||
|
ch->dirty |= POCKETMOD_PITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_volume_slide(_pocketmod_chan *ch, int param)
|
||||||
|
{
|
||||||
|
/* Undocumented quirk: If both x and y are nonzero, then the value of x */
|
||||||
|
/* takes precedence. (Yes, there are songs that rely on this behavior.) */
|
||||||
|
int change = (param & 0xf0) ? (param >> 4) : -(param & 0x0f);
|
||||||
|
ch->volume = _pocketmod_clamp_volume(ch->volume + change);
|
||||||
|
ch->dirty |= POCKETMOD_VOLUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_next_line(pocketmod_context *c)
|
||||||
|
{
|
||||||
|
unsigned char (*data)[4];
|
||||||
|
int i, pos, pattern_break = -1;
|
||||||
|
|
||||||
|
/* When entering a new pattern order index, mark it as "visited" */
|
||||||
|
if (c->line == 0) {
|
||||||
|
c->visited[c->pattern >> 3] |= 1 << (c->pattern & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move to the next pattern if this was the last line */
|
||||||
|
if (++c->line == 64) {
|
||||||
|
if (++c->pattern == c->length) {
|
||||||
|
c->pattern = c->reset;
|
||||||
|
}
|
||||||
|
c->line = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the pattern data for the current line */
|
||||||
|
pos = (c->order[c->pattern] * 64 + c->line) * c->num_channels * 4;
|
||||||
|
data = (unsigned char(*)[4]) (c->patterns + pos);
|
||||||
|
for (i = 0; i < c->num_channels; i++) {
|
||||||
|
|
||||||
|
/* Decode columns */
|
||||||
|
int sample = (data[i][0] & 0xf0) | (data[i][2] >> 4);
|
||||||
|
int period = ((data[i][0] & 0x0f) << 8) | data[i][1];
|
||||||
|
int effect = ((data[i][2] & 0x0f) << 8) | data[i][3];
|
||||||
|
|
||||||
|
/* Memorize effect parameter values */
|
||||||
|
_pocketmod_chan *ch = &c->channels[i];
|
||||||
|
ch->effect = (effect >> 8) != 0xe ? (effect >> 8) : (effect >> 4);
|
||||||
|
ch->param = (effect >> 8) != 0xe ? (effect & 0xff) : (effect & 0x0f);
|
||||||
|
|
||||||
|
/* Set sample */
|
||||||
|
if (sample) {
|
||||||
|
if (sample <= POCKETMOD_MAX_SAMPLES) {
|
||||||
|
unsigned char *sample_data = POCKETMOD_SAMPLE(c, sample);
|
||||||
|
ch->sample = sample;
|
||||||
|
ch->finetune = sample_data[2] & 0x0f;
|
||||||
|
ch->volume = _pocketmod_min(sample_data[3], 0x40);
|
||||||
|
if (ch->effect != 0xED) {
|
||||||
|
ch->dirty |= POCKETMOD_VOLUME;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ch->sample = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set note */
|
||||||
|
if (period) {
|
||||||
|
int note = _pocketmod_period_to_note(period);
|
||||||
|
period += _pocketmod_finetune[ch->finetune][note];
|
||||||
|
if (ch->effect != 0x3) {
|
||||||
|
if (ch->effect != 0xED) {
|
||||||
|
ch->period = period;
|
||||||
|
ch->dirty |= POCKETMOD_PITCH;
|
||||||
|
ch->position = 0.0f;
|
||||||
|
ch->lfo_step = 0;
|
||||||
|
} else {
|
||||||
|
ch->delayed = period;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle pattern effects */
|
||||||
|
switch (ch->effect) {
|
||||||
|
|
||||||
|
/* Memorize parameters */
|
||||||
|
case 0x3: POCKETMOD_MEM(ch->param3, ch->param); /* Fall through */
|
||||||
|
case 0x5: POCKETMOD_MEM(ch->target, period); break;
|
||||||
|
case 0x4: POCKETMOD_MEM2(ch->param4, ch->param); break;
|
||||||
|
case 0x7: POCKETMOD_MEM2(ch->param7, ch->param); break;
|
||||||
|
case 0xE1: POCKETMOD_MEM(ch->paramE1, ch->param); break;
|
||||||
|
case 0xE2: POCKETMOD_MEM(ch->paramE2, ch->param); break;
|
||||||
|
case 0xEA: POCKETMOD_MEM(ch->paramEA, ch->param); break;
|
||||||
|
case 0xEB: POCKETMOD_MEM(ch->paramEB, ch->param); break;
|
||||||
|
|
||||||
|
/* 8xx: Set stereo balance (nonstandard) */
|
||||||
|
case 0x8: {
|
||||||
|
ch->balance = ch->param;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* 9xx: Set sample offset */
|
||||||
|
case 0x9: {
|
||||||
|
if (period != 0 || sample != 0) {
|
||||||
|
ch->param9 = ch->param ? ch->param : ch->param9;
|
||||||
|
ch->position = ch->param9 << 8;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* Bxx: Jump to pattern */
|
||||||
|
case 0xB: {
|
||||||
|
c->pattern = ch->param < c->length ? ch->param : 0;
|
||||||
|
c->line = -1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* Cxx: Set volume */
|
||||||
|
case 0xC: {
|
||||||
|
ch->volume = _pocketmod_clamp_volume(ch->param);
|
||||||
|
ch->dirty |= POCKETMOD_VOLUME;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* Dxy: Pattern break */
|
||||||
|
case 0xD: {
|
||||||
|
pattern_break = (ch->param >> 4) * 10 + (ch->param & 15);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* E4x: Set vibrato waveform */
|
||||||
|
case 0xE4: {
|
||||||
|
ch->lfo_type[0] = ch->param;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* E5x: Set sample finetune */
|
||||||
|
case 0xE5: {
|
||||||
|
ch->finetune = ch->param;
|
||||||
|
ch->dirty |= POCKETMOD_PITCH;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* E6x: Pattern loop */
|
||||||
|
case 0xE6: {
|
||||||
|
if (ch->param) {
|
||||||
|
if (!ch->loop_count) {
|
||||||
|
ch->loop_count = ch->param;
|
||||||
|
c->line = ch->loop_line;
|
||||||
|
} else if (--ch->loop_count) {
|
||||||
|
c->line = ch->loop_line;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ch->loop_line = c->line - 1;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* E7x: Set tremolo waveform */
|
||||||
|
case 0xE7: {
|
||||||
|
ch->lfo_type[1] = ch->param;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* E8x: Set stereo balance (nonstandard) */
|
||||||
|
case 0xE8: {
|
||||||
|
ch->balance = ch->param << 4;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* EEx: Pattern delay */
|
||||||
|
case 0xEE: {
|
||||||
|
c->pattern_delay = ch->param;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* Fxx: Set speed */
|
||||||
|
case 0xF: {
|
||||||
|
if (ch->param != 0) {
|
||||||
|
if (ch->param < 0x20) {
|
||||||
|
c->ticks_per_line = ch->param;
|
||||||
|
} else {
|
||||||
|
float rate = c->samples_per_second;
|
||||||
|
c->samples_per_tick = rate / (0.4f * ch->param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pattern breaks are handled here, so that only one jump happens even */
|
||||||
|
/* when multiple Dxy commands appear on the same line. (You guessed it: */
|
||||||
|
/* There are songs that rely on this behavior!) */
|
||||||
|
if (pattern_break != -1) {
|
||||||
|
c->line = (pattern_break < 64 ? pattern_break : 0) - 1;
|
||||||
|
if (++c->pattern == c->length) {
|
||||||
|
c->pattern = c->reset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_next_tick(pocketmod_context *c)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Move to the next line if this was the last tick */
|
||||||
|
if (++c->tick == c->ticks_per_line) {
|
||||||
|
if (c->pattern_delay > 0) {
|
||||||
|
c->pattern_delay--;
|
||||||
|
} else {
|
||||||
|
_pocketmod_next_line(c);
|
||||||
|
}
|
||||||
|
c->tick = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make per-tick adjustments for all channels */
|
||||||
|
for (i = 0; i < c->num_channels; i++) {
|
||||||
|
_pocketmod_chan *ch = &c->channels[i];
|
||||||
|
int param = ch->param;
|
||||||
|
|
||||||
|
/* Advance the LFO random number generator */
|
||||||
|
c->lfo_rng = 0x0019660d * c->lfo_rng + 0x3c6ef35f;
|
||||||
|
|
||||||
|
/* Handle effects that may happen on any tick of a line */
|
||||||
|
switch (ch->effect) {
|
||||||
|
|
||||||
|
/* 0xy: Arpeggio */
|
||||||
|
case 0x0: {
|
||||||
|
ch->dirty |= POCKETMOD_PITCH;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* E9x: Retrigger note every x ticks */
|
||||||
|
case 0xE9: {
|
||||||
|
if (!(param && c->tick % param)) {
|
||||||
|
ch->position = 0.0f;
|
||||||
|
ch->lfo_step = 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* ECx: Cut note after x ticks */
|
||||||
|
case 0xEC: {
|
||||||
|
if (c->tick == param) {
|
||||||
|
ch->volume = 0;
|
||||||
|
ch->dirty |= POCKETMOD_VOLUME;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* EDx: Delay note for x ticks */
|
||||||
|
case 0xED: {
|
||||||
|
if (c->tick == param && ch->sample) {
|
||||||
|
ch->dirty |= POCKETMOD_VOLUME | POCKETMOD_PITCH;
|
||||||
|
ch->period = ch->delayed;
|
||||||
|
ch->position = 0.0f;
|
||||||
|
ch->lfo_step = 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle effects that only happen on the first tick of a line */
|
||||||
|
if (c->tick == 0) {
|
||||||
|
switch (ch->effect) {
|
||||||
|
case 0xE1: _pocketmod_pitch_slide(ch, -ch->paramE1); break;
|
||||||
|
case 0xE2: _pocketmod_pitch_slide(ch, +ch->paramE2); break;
|
||||||
|
case 0xEA: _pocketmod_volume_slide(ch, ch->paramEA << 4); break;
|
||||||
|
case 0xEB: _pocketmod_volume_slide(ch, ch->paramEB & 15); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle effects that are not applied on the first tick of a line */
|
||||||
|
} else {
|
||||||
|
switch (ch->effect) {
|
||||||
|
|
||||||
|
/* 1xx: Portamento up */
|
||||||
|
case 0x1: {
|
||||||
|
_pocketmod_pitch_slide(ch, -param);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* 2xx: Portamento down */
|
||||||
|
case 0x2: {
|
||||||
|
_pocketmod_pitch_slide(ch, +param);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* 5xy: Volume slide + tone portamento */
|
||||||
|
case 0x5: {
|
||||||
|
_pocketmod_volume_slide(ch, param);
|
||||||
|
} /* Fall through */
|
||||||
|
|
||||||
|
/* 3xx: Tone portamento */
|
||||||
|
case 0x3: {
|
||||||
|
int rate = ch->param3;
|
||||||
|
int order = ch->period < ch->target;
|
||||||
|
int closer = ch->period + (order ? rate : -rate);
|
||||||
|
int new_order = closer < ch->target;
|
||||||
|
ch->period = new_order == order ? closer : ch->target;
|
||||||
|
ch->dirty |= POCKETMOD_PITCH;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* 6xy: Volume slide + vibrato */
|
||||||
|
case 0x6: {
|
||||||
|
_pocketmod_volume_slide(ch, param);
|
||||||
|
} /* Fall through */
|
||||||
|
|
||||||
|
/* 4xy: Vibrato */
|
||||||
|
case 0x4: {
|
||||||
|
ch->lfo_step++;
|
||||||
|
ch->dirty |= POCKETMOD_PITCH;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* 7xy: Tremolo */
|
||||||
|
case 0x7: {
|
||||||
|
ch->lfo_step++;
|
||||||
|
ch->dirty |= POCKETMOD_VOLUME;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* Axy: Volume slide */
|
||||||
|
case 0xA: {
|
||||||
|
_pocketmod_volume_slide(ch, param);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update channel volume/pitch if either is out of date */
|
||||||
|
if (ch->dirty & POCKETMOD_VOLUME) { _pocketmod_update_volume(c, ch); }
|
||||||
|
if (ch->dirty & POCKETMOD_PITCH) { _pocketmod_update_pitch(c, ch); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pocketmod_render_channel(pocketmod_context *c,
|
||||||
|
_pocketmod_chan *chan,
|
||||||
|
float *output,
|
||||||
|
int samples_to_write)
|
||||||
|
{
|
||||||
|
/* Gather some loop data */
|
||||||
|
_pocketmod_sample *sample = &c->samples[chan->sample - 1];
|
||||||
|
unsigned char *data = POCKETMOD_SAMPLE(c, chan->sample);
|
||||||
|
const int loop_start = ((data[4] << 8) | data[5]) << 1;
|
||||||
|
const int loop_length = ((data[6] << 8) | data[7]) << 1;
|
||||||
|
const int loop_end = loop_length > 2 ? loop_start + loop_length : 0xffffff;
|
||||||
|
const float sample_end = 1 + _pocketmod_min(loop_end, sample->length);
|
||||||
|
|
||||||
|
/* Calculate left/right levels */
|
||||||
|
const float volume = chan->real_volume / (float) (128 * 64 * 4);
|
||||||
|
const float level_l = volume * (1.0f - chan->balance / 255.0f);
|
||||||
|
const float level_r = volume * (0.0f + chan->balance / 255.0f);
|
||||||
|
|
||||||
|
/* Write samples */
|
||||||
|
int i, num;
|
||||||
|
do {
|
||||||
|
|
||||||
|
/* Calculate how many samples we can write in one go */
|
||||||
|
num = (int)((sample_end - chan->position) / chan->increment);
|
||||||
|
num = _pocketmod_min(num, samples_to_write);
|
||||||
|
|
||||||
|
/* Resample and write 'num' samples */
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
int x0 = (int)chan->position;
|
||||||
|
#ifdef POCKETMOD_NO_INTERPOLATION
|
||||||
|
float s = sample->data[x0];
|
||||||
|
#else
|
||||||
|
int x1 = x0 + 1 - loop_length * (x0 + 1 >= loop_end);
|
||||||
|
float t = chan->position - x0;
|
||||||
|
float s = (1.0f - t) * sample->data[x0] + t * sample->data[x1];
|
||||||
|
#endif
|
||||||
|
chan->position += chan->increment;
|
||||||
|
*output++ += level_l * s;
|
||||||
|
*output++ += level_r * s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rewind the sample when reaching the loop point */
|
||||||
|
if (chan->position >= loop_end) {
|
||||||
|
chan->position -= loop_length;
|
||||||
|
|
||||||
|
/* Cut the sample if the end is reached */
|
||||||
|
} else if (chan->position >= sample->length) {
|
||||||
|
chan->position = -1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples_to_write -= num;
|
||||||
|
} while (num > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _pocketmod_ident(pocketmod_context *c, unsigned char *data, int size)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
/* 31-instrument files are at least 1084 bytes long */
|
||||||
|
if (size >= 1084) {
|
||||||
|
|
||||||
|
/* The format tag is located at offset 1080 */
|
||||||
|
unsigned char *tag = data + 1080;
|
||||||
|
|
||||||
|
/* List of recognized format tags (possibly incomplete) */
|
||||||
|
static const struct {
|
||||||
|
char name[5];
|
||||||
|
char channels;
|
||||||
|
} tags[] = {
|
||||||
|
/* TODO: FLT8 intentionally omitted because I haven't been able */
|
||||||
|
/* to find a specimen to test its funky pattern pairing format */
|
||||||
|
{"M.K.", 4}, {"M!K!", 4}, {"FLT4", 4}, {"4CHN", 4},
|
||||||
|
{"OKTA", 8}, {"OCTA", 8}, {"CD81", 8}, {"FA08", 8},
|
||||||
|
{"1CHN", 1}, {"2CHN", 2}, {"3CHN", 3}, {"4CHN", 4},
|
||||||
|
{"5CHN", 5}, {"6CHN", 6}, {"7CHN", 7}, {"8CHN", 8},
|
||||||
|
{"9CHN", 9}, {"10CH", 10}, {"11CH", 11}, {"12CH", 12},
|
||||||
|
{"13CH", 13}, {"14CH", 14}, {"15CH", 15}, {"16CH", 16},
|
||||||
|
{"17CH", 17}, {"18CH", 18}, {"19CH", 19}, {"20CH", 20},
|
||||||
|
{"21CH", 21}, {"22CH", 22}, {"23CH", 23}, {"24CH", 24},
|
||||||
|
{"25CH", 25}, {"26CH", 26}, {"27CH", 27}, {"28CH", 28},
|
||||||
|
{"29CH", 29}, {"30CH", 30}, {"31CH", 31}, {"32CH", 32}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check the format tag to determine if this is a 31-sample MOD */
|
||||||
|
for (i = 0; i < (int) (sizeof(tags) / sizeof(*tags)); i++) {
|
||||||
|
if (tags[i].name[0] == tag[0] && tags[i].name[1] == tag[1]
|
||||||
|
&& tags[i].name[2] == tag[2] && tags[i].name[3] == tag[3]) {
|
||||||
|
c->num_channels = tags[i].channels;
|
||||||
|
c->length = data[950];
|
||||||
|
c->reset = data[951];
|
||||||
|
c->order = &data[952];
|
||||||
|
c->patterns = &data[1084];
|
||||||
|
c->num_samples = 31;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A 15-instrument MOD has to be at least 600 bytes long */
|
||||||
|
if (size < 600) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the song title only contains ASCII bytes (or null) */
|
||||||
|
for (i = 0; i < 20; i++) {
|
||||||
|
if (data[i] != '\0' && (data[i] < ' ' || data[i] > '~')) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that sample names only contain ASCII bytes (or null) */
|
||||||
|
for (i = 0; i < 15; i++) {
|
||||||
|
for (j = 0; j < 22; j++) {
|
||||||
|
char chr = data[20 + i * 30 + j];
|
||||||
|
if (chr != '\0' && (chr < ' ' || chr > '~')) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It looks like we have an older 15-instrument MOD */
|
||||||
|
c->length = data[470];
|
||||||
|
c->reset = data[471];
|
||||||
|
c->order = &data[472];
|
||||||
|
c->patterns = &data[600];
|
||||||
|
c->num_samples = 15;
|
||||||
|
c->num_channels = 4;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate)
|
||||||
|
{
|
||||||
|
int i, remaining, header_bytes, pattern_bytes;
|
||||||
|
unsigned char *byte = (unsigned char*) c;
|
||||||
|
signed char *sample_data;
|
||||||
|
|
||||||
|
/* Check that arguments look more or less sane */
|
||||||
|
if (!c || !data || rate <= 0 || size <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero out the whole context and identify the MOD type */
|
||||||
|
_pocketmod_zero(c, sizeof(pocketmod_context));
|
||||||
|
c->source = (unsigned char*) data;
|
||||||
|
if (!_pocketmod_ident(c, c->source, size)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that we are compiled with support for enough channels */
|
||||||
|
if (c->num_channels > POCKETMOD_MAX_CHANNELS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that we have enough sample slots for this file */
|
||||||
|
if (POCKETMOD_MAX_SAMPLES < 31) {
|
||||||
|
byte = (unsigned char*) data + 20;
|
||||||
|
for (i = 0; i < c->num_samples; i++) {
|
||||||
|
unsigned int length = 2 * ((byte[22] << 8) | byte[23]);
|
||||||
|
if (i >= POCKETMOD_MAX_SAMPLES && length > 2) {
|
||||||
|
return 0; /* Can't fit this sample */
|
||||||
|
}
|
||||||
|
byte += 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the song length is in valid range (1..128) */
|
||||||
|
if (c->length == 0 || c->length > 128) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure that the reset pattern doesn't take us out of bounds */
|
||||||
|
if (c->reset >= c->length) {
|
||||||
|
c->reset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Count how many patterns there are in the file */
|
||||||
|
c->num_patterns = 0;
|
||||||
|
for (i = 0; i < 128 && c->order[i] < 128; i++) {
|
||||||
|
c->num_patterns = _pocketmod_max(c->num_patterns, c->order[i]);
|
||||||
|
}
|
||||||
|
pattern_bytes = 256 * c->num_channels * ++c->num_patterns;
|
||||||
|
header_bytes = (int) ((char*) c->patterns - (char*) data);
|
||||||
|
|
||||||
|
/* Check that each pattern in the order is within file bounds */
|
||||||
|
for (i = 0; i < c->length; i++) {
|
||||||
|
if (header_bytes + 256 * c->num_channels * c->order[i] > size) {
|
||||||
|
return 0; /* Reading this pattern would be a buffer over-read! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the pattern data doesn't extend past the end of the file */
|
||||||
|
if (header_bytes + pattern_bytes > size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load sample payload data, truncating ones that extend outside the file */
|
||||||
|
remaining = size - header_bytes - pattern_bytes;
|
||||||
|
sample_data = (signed char*) data + header_bytes + pattern_bytes;
|
||||||
|
for (i = 0; i < c->num_samples; i++) {
|
||||||
|
unsigned char *data = POCKETMOD_SAMPLE(c, i + 1);
|
||||||
|
unsigned int length = ((data[0] << 8) | data[1]) << 1;
|
||||||
|
_pocketmod_sample *sample = &c->samples[i];
|
||||||
|
sample->data = sample_data;
|
||||||
|
sample->length = _pocketmod_min(length > 2 ? length : 0, remaining);
|
||||||
|
sample_data += sample->length;
|
||||||
|
remaining -= sample->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up ProTracker default panning for all channels */
|
||||||
|
for (i = 0; i < c->num_channels; i++) {
|
||||||
|
c->channels[i].balance = 0x80 + ((((i + 1) >> 1) & 1) ? 0x20 : -0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare to render from the start */
|
||||||
|
c->ticks_per_line = 6;
|
||||||
|
c->samples_per_second = rate;
|
||||||
|
c->samples_per_tick = rate / 50.0f;
|
||||||
|
c->lfo_rng = 0xbadc0de;
|
||||||
|
c->line = -1;
|
||||||
|
c->tick = c->ticks_per_line - 1;
|
||||||
|
_pocketmod_next_tick(c);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pocketmod_render(pocketmod_context *c, void *buffer, int buffer_size)
|
||||||
|
{
|
||||||
|
int i, samples_rendered = 0;
|
||||||
|
int samples_remaining = buffer_size / POCKETMOD_SAMPLE_SIZE;
|
||||||
|
if (c && buffer) {
|
||||||
|
float (*output)[2] = (float(*)[2]) buffer;
|
||||||
|
while (samples_remaining > 0) {
|
||||||
|
|
||||||
|
/* Calculate the number of samples left in this tick */
|
||||||
|
int num = (int) (c->samples_per_tick - c->sample);
|
||||||
|
num = _pocketmod_min(num + !num, samples_remaining);
|
||||||
|
|
||||||
|
/* Render and mix 'num' samples from each channel */
|
||||||
|
_pocketmod_zero(output, num * POCKETMOD_SAMPLE_SIZE);
|
||||||
|
for (i = 0; i < c->num_channels; i++) {
|
||||||
|
_pocketmod_chan *chan = &c->channels[i];
|
||||||
|
if (chan->sample != 0 && chan->position >= 0.0f) {
|
||||||
|
_pocketmod_render_channel(c, chan, *output, num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
samples_remaining -= num;
|
||||||
|
samples_rendered += num;
|
||||||
|
output += num;
|
||||||
|
|
||||||
|
/* Advance song position by 'num' samples */
|
||||||
|
if ((c->sample += num) >= c->samples_per_tick) {
|
||||||
|
c->sample -= c->samples_per_tick;
|
||||||
|
_pocketmod_next_tick(c);
|
||||||
|
|
||||||
|
/* Stop if a new pattern was reached */
|
||||||
|
if (c->line == 0 && c->tick == 0) {
|
||||||
|
|
||||||
|
/* Increment loop counter as needed */
|
||||||
|
if (c->visited[c->pattern >> 3] & (1 << (c->pattern & 7))) {
|
||||||
|
_pocketmod_zero(c->visited, sizeof(c->visited));
|
||||||
|
c->loop_count++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return samples_rendered * POCKETMOD_SAMPLE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pocketmod_loop_count(pocketmod_context *c)
|
||||||
|
{
|
||||||
|
return c->loop_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* #ifdef POCKETMOD_IMPLEMENTATION */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* #ifndef POCKETMOD_H_INCLUDED */
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 rombankzero
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
BIN
joeylib/src/applause.wav
(Stored with Git LFS)
Normal file
BIN
joeylib/src/applause.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
File diff suppressed because it is too large
Load diff
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
JoeyLib NTP Player
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "song.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongT - A pointer to single song, including it’s instruments
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
} jlSong_t;
|
|
||||||
|
|
||||||
typedef jlSong_t* jlSongT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Startup Song Library
|
|
||||||
*/
|
|
||||||
void jlSongStartup()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Shutdown Song Library
|
|
||||||
*/
|
|
||||||
void jlSongShutdown()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongLoad - Loads a song into memory, and into DOC RAM, so that it's ready
|
|
||||||
to play
|
|
||||||
*/
|
|
||||||
jlSongT jlSongLoad(const char* pSongPath)
|
|
||||||
{
|
|
||||||
jlSongT result = NULL;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongLoadAt - Load a song from memory into DOC RAM
|
|
||||||
DO NOT FREE THIS MEMORY UNTIL AFTER A CALL TO jlSongFree
|
|
||||||
*/
|
|
||||||
jlSongT jlSongLoadAt(const char* pSongInRAM)
|
|
||||||
{
|
|
||||||
jlSongT result = NULL;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongPlay
|
|
||||||
jlSongT song - song handle
|
|
||||||
jlBool bLoop - Play once, or Loop continuously
|
|
||||||
*/
|
|
||||||
void jlSongPlay(jlSongT song, jlBool bLoop)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Stop the current song
|
|
||||||
*/
|
|
||||||
void jlSongStop(jlSongT song)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
bPaused to TRUE to pause the song
|
|
||||||
bPaused to FALSE to result song from where it is
|
|
||||||
*/
|
|
||||||
void jlSongSetPause(jlSongT song, jlBool bPaused)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Volume from 0 which is silent, to 255 which is the loudest
|
|
||||||
*/
|
|
||||||
void jlSongSetVolume(jlSongT song, U8 volume)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// 0x100 is a tempo of 1x, and is the default
|
|
||||||
// 0x080 will run the tempo at half speed
|
|
||||||
// 0x200 will run the tempo at 2X
|
|
||||||
*/
|
|
||||||
void jlSongSetTempo(jlSongT song, U16 tempo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Release memory and resources
|
|
||||||
*/
|
|
||||||
void jlSongFree(jlSontT song)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* JL_SONG_H */
|
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
JoeyLib NTP Player
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef JL_SONG_H
|
|
||||||
#define JL_SONG_H 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongT - A pointer to single song, including it’s instruments
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
} jlSong_t;
|
|
||||||
|
|
||||||
typedef jlSong_t* jlSongT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Startup Song Library
|
|
||||||
*/
|
|
||||||
void jlSongStartup();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Shutdown Song Library
|
|
||||||
*/
|
|
||||||
void jlSongShutdown();
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongLoad - Loads a song into memory, and into DOC RAM, so that it's ready
|
|
||||||
to play
|
|
||||||
*/
|
|
||||||
jlSongT jlSongLoad(const char* pSongPath);
|
|
||||||
/*
|
|
||||||
jlSongLoadAt - Load a song from memory into DOC RAM
|
|
||||||
DO NOT FREE THIS MEMORY UNTIL AFTER A CALL TO jlSongFree
|
|
||||||
*/
|
|
||||||
jlSongT jlSongLoadAt(const char* pSongInRAM);
|
|
||||||
|
|
||||||
/*
|
|
||||||
jlSongPlay
|
|
||||||
jlSongT song - song handle
|
|
||||||
jlBool bLoop - Play once, or Loop continuously
|
|
||||||
*/
|
|
||||||
void jlSongPlay(jlSongT song, jlBool bLoop);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Stop the current song
|
|
||||||
*/
|
|
||||||
void jlSongStop(jlSongT song);
|
|
||||||
|
|
||||||
/*
|
|
||||||
bPaused to TRUE to pause the song
|
|
||||||
bPaused to FALSE to result song from where it is
|
|
||||||
*/
|
|
||||||
void jlSongSetPause(jlSongT song, jlBool bPaused);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Volume from 0 which is silent, to 255 which is the loudest
|
|
||||||
*/
|
|
||||||
void jlSongSetVolume(jlSongT song, U8 volume);
|
|
||||||
|
|
||||||
/*
|
|
||||||
// 0x100 is a tempo of 1x, and is the default
|
|
||||||
// 0x080 will run the tempo at half speed
|
|
||||||
// 0x200 will run the tempo at 2X
|
|
||||||
*/
|
|
||||||
void jlSongSetTempo(jlSongT song, U16 tempo);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Release memory and resources
|
|
||||||
*/
|
|
||||||
void jlSongFree(jlSontT song);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* JL_SONG_H */
|
|
||||||
|
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
/*
|
|
||||||
JoeyLib Sound for the IIgs
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "sound.h"
|
|
||||||
|
|
||||||
#ifndef NULL
|
|
||||||
#define NULL 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Call this before any other Sound Function */
|
|
||||||
void jlSoundStartup()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call this when done, for proper shutdown */
|
|
||||||
void jlSoundShutdown()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Load .jla Sound Bank into memory, or into DOC RAM
|
|
||||||
*/
|
|
||||||
jlSoundT jlSoundLoad(const char* pSoundBankPath)
|
|
||||||
{
|
|
||||||
jlSoundT result = NULL;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Load .jla Sound Bank from memory, into DOC RAM
|
|
||||||
DO NOT FREE THIS MEMORY (pointers in the library will point into it)
|
|
||||||
it may be free after the jlSoundFree hsa been called
|
|
||||||
*/
|
|
||||||
jlSoundT jlSoundLoadAt(const char* pBankInRAM)
|
|
||||||
{
|
|
||||||
jlSoundT result = NULL;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Returns back an index into the audio definition table, which is required
|
|
||||||
in order to call jlSoundPlay
|
|
||||||
|
|
||||||
Returns -1 if the sound isn't found
|
|
||||||
*/
|
|
||||||
signed short jlSoundFind(jlSoundT soundbank, const char* sfxName)
|
|
||||||
{
|
|
||||||
signed short result = -1;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Play a Sound
|
|
||||||
*/
|
|
||||||
void jlSoundPlay(jlSoundT soundbank, signed short audio_index)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return the number of SFX in this Bank
|
|
||||||
*/
|
|
||||||
signed short jlSoundGetNumSFX(jlSoundT soundbank)
|
|
||||||
{
|
|
||||||
signed short result = 0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return a C string name of the sound effect with this index
|
|
||||||
DO NOT FREE THIS POINTER
|
|
||||||
*/
|
|
||||||
char *jlSoundGetName(signed short audio_index)
|
|
||||||
{
|
|
||||||
char* pResult = NULL;
|
|
||||||
return pResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Called when you are done with this soundbank
|
|
||||||
*/
|
|
||||||
void jlSoundFree(jlSoundT soundbank)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
JoeyLib Sound Header File, with Sound Function Prototypes
|
|
||||||
*/
|
|
||||||
#ifndef JL_SOUND_H
|
|
||||||
#define JL_SOUND_H
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
|
|
||||||
} jlSound_t;
|
|
||||||
|
|
||||||
/* jlSoundT - A pointer to a sound bank / collection of sounds */
|
|
||||||
typedef jlSound_t* jlSoundT;
|
|
||||||
|
|
||||||
/* Call this before any other Sound Function */
|
|
||||||
void jlSoundStartup();
|
|
||||||
/* Call this when done, for proper shutdown */
|
|
||||||
void jlSoundShutdown();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Load .jla Sound Bank into memory, or into DOC RAM
|
|
||||||
*/
|
|
||||||
jlSoundT jlSoundLoad(const char* pSoundBankPath);
|
|
||||||
/*
|
|
||||||
Load .jla Sound Bank from memory, into DOC RAM
|
|
||||||
DO NOT FREE THIS MEMORY (pointers in the library will point into it)
|
|
||||||
it may be free after the jlSoundFree hsa been called
|
|
||||||
*/
|
|
||||||
jlSoundT jlSoundLoadAt(const char* pBankInRAM);
|
|
||||||
/*
|
|
||||||
Returns back an index into the audio definition table, which is required
|
|
||||||
in order to call jlSoundPlay
|
|
||||||
*/
|
|
||||||
signed short jlSoundFind(jlSoundT soundbank, const char* sfxName);
|
|
||||||
/*
|
|
||||||
Play a Sound
|
|
||||||
*/
|
|
||||||
void jlSoundPlay(jlSoundT soundbank, signed short audio_index);
|
|
||||||
/*
|
|
||||||
Return the number of SFX in this Bank
|
|
||||||
*/
|
|
||||||
signed short jlSoundGetNumSFX(jlSoundT soundbank);
|
|
||||||
/*
|
|
||||||
Return a C string name of the sound effect with this index
|
|
||||||
DO NOT FREE THIS POINTER
|
|
||||||
*/
|
|
||||||
char *jlSoundGetName(signed short audio_index);
|
|
||||||
/*
|
|
||||||
Called when you are done with this soundbank
|
|
||||||
*/
|
|
||||||
void jlSoundFree(jlSoundT soundbank);
|
|
||||||
|
|
||||||
#endif /* JL_SOUND_H */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
MACRO
&lab _pushpage
&lab lcla &addr
&addr seta 254
lcla &loop
&loop seta 128
.ploop
pei &addr
&addr seta &addr-2
&loop seta &loop-1
aif &loop,^ploop
MEND
macro
&lab asl4
&lab lcla &ct
&ct seta 4
.top
asl a
&ct seta &ct-1
aif &ct>0,^top
mend
macro
&lab _auxON
&lab lda >$00C068
ora #$0030
sta >$00C068
mend
macro
&lab _auxOFF
&lab lda >$00C068
and #$FFCF
sta >$00C068
mend
macro
&lab _shadowON
&lab lda >$00C035
and #$FFF7
sta >$00C035
mend
macro
&lab _shadowOFF
&lab lda >$00C035
ora #$0008
sta >$00C035
mend
|
|
|
@ -27,12 +27,10 @@
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <misctool.h>
|
#include <misctool.h>
|
||||||
#include <sound.h>
|
#include <sound.h>
|
||||||
#include <MIDISynth.h>
|
|
||||||
#include <Orca.h>
|
#include <Orca.h>
|
||||||
#undef false
|
|
||||||
#undef true
|
|
||||||
|
|
||||||
|
|
||||||
|
#define JOEY_MAIN
|
||||||
#include "joey.h"
|
#include "joey.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,6 +39,9 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define MUSIC_SLOTS 25
|
||||||
|
|
||||||
|
|
||||||
#define DPForSound 0x0000 // Needs one direct page
|
#define DPForSound 0x0000 // Needs one direct page
|
||||||
#define DPTotal 0x0100 // Total size of all tool DP usage
|
#define DPTotal 0x0100 // Total size of all tool DP usage
|
||||||
|
|
||||||
|
@ -66,28 +67,42 @@ extern void asmStop(void);
|
||||||
extern pascal void NTPBootInit(void) inline(0x01DE, dispatcher);
|
extern pascal void NTPBootInit(void) inline(0x01DE, dispatcher);
|
||||||
extern pascal void NTPStartUp(Word) inline(0x02DE, dispatcher);
|
extern pascal void NTPStartUp(Word) inline(0x02DE, dispatcher);
|
||||||
extern pascal void NTPShutDown(void) inline(0x03DE, dispatcher);
|
extern pascal void NTPShutDown(void) inline(0x03DE, dispatcher);
|
||||||
|
extern pascal Word NTPGetVersion(void) inline(0x04DE, dispatcher);
|
||||||
|
extern pascal void NTPReset(void) inline(0x05DE, dispatcher);
|
||||||
|
extern pascal Word NTPStatus(void) inline(0x06DE, dispatcher);
|
||||||
|
extern pascal void NTPStopMusic(void) inline(0x08DE, dispatcher);
|
||||||
extern pascal void NTPLoadOneMusic(Pointer) inline(0x09DE, dispatcher);
|
extern pascal void NTPLoadOneMusic(Pointer) inline(0x09DE, dispatcher);
|
||||||
extern pascal void NTPPlayMusic(Word) inline(0x0ADE, dispatcher);
|
extern pascal void NTPPlayMusic(Word) inline(0x0ADE, dispatcher);
|
||||||
|
extern pascal Word NTPGetEndOfMusic(void) inline(0x0CDE, dispatcher);
|
||||||
|
extern pascal void NTPAddToBatch(Pointer, Word) inline(0x0DDE, dispatcher);
|
||||||
|
extern pascal void NTPSelectBatch(Word) inline(0x0EDE, dispatcher);
|
||||||
|
extern pascal void NTPKillBatch(Word) inline(0x0FDE, dispatcher);
|
||||||
extern pascal Word NTPGetPlayingMusic(void) inline(0x10DE, dispatcher);
|
extern pascal Word NTPGetPlayingMusic(void) inline(0x10DE, dispatcher);
|
||||||
|
extern pascal void NTPPlayBatch(Pointer) inline(0x11DE, dispatcher);
|
||||||
|
extern pascal Pointer NTPGetTrackVu(void) inline(0x12DE, dispatcher);
|
||||||
extern pascal void NTPPauseMusic(void) inline(0x13DE, dispatcher);
|
extern pascal void NTPPauseMusic(void) inline(0x13DE, dispatcher);
|
||||||
extern pascal void NTPContinueMusic(void) inline(0x14DE, dispatcher);
|
extern pascal void NTPContinueMusic(void) inline(0x14DE, dispatcher);
|
||||||
|
|
||||||
|
|
||||||
|
extern jbool _jlSwapChannels;
|
||||||
|
|
||||||
|
|
||||||
char _jlKeyCheck(char key);
|
char _jlKeyCheck(char key);
|
||||||
|
|
||||||
|
|
||||||
static byte *KEYBOARD = (byte *)0x00C000L;
|
static jbyte *KEYBOARD = (jbyte *)0x00C000L;
|
||||||
static byte *KEYSTROBE = (byte *)0x00C010L;
|
static jbyte *KEYSTROBE = (jbyte *)0x00C010L;
|
||||||
static byte *BUTTON0 = (byte *)0x00C061L;
|
static jbyte *BUTTON0 = (jbyte *)0x00C061L;
|
||||||
static byte *BUTTON1 = (byte *)0x00C062L;
|
static jbyte *BUTTON1 = (jbyte *)0x00C062L;
|
||||||
static jlPixelPairT *SHRPIXELS = (jlPixelPairT *)0x012000L; // Shadow of 0xE12000
|
static jlPixelPairT *SHRPIXELS = (jlPixelPairT *)0x012000L; // Shadow of 0xE12000
|
||||||
static byte *SHRSCB = (byte *)0x019D00L; // Shadow of 0xE19D00
|
static jbyte *SHRSCB = (jbyte *)0x019D00L; // Shadow of 0xE19D00
|
||||||
static jlColorT *SHRCOLORS = (jlColorT *)0x019E00L; // Shadow of 0xE19E00
|
static jlColorT *SHRCOLORS = (jlColorT *)0x019E00L; // Shadow of 0xE19E00
|
||||||
static byte *BORDER = (byte *)0xE0C034L;
|
static jbyte *BORDER = (jbyte *)0xE0C034L;
|
||||||
|
|
||||||
|
|
||||||
static byte _jlBorderSaved;
|
static jbyte _jlBorderSaved;
|
||||||
static jint16 _jlHertz;
|
static jint16 _jlHertz;
|
||||||
|
static jint16 _jlMusicList[MUSIC_SLOTS];
|
||||||
|
|
||||||
|
|
||||||
#ifdef JOEY_DEBUG
|
#ifdef JOEY_DEBUG
|
||||||
|
@ -129,7 +144,7 @@ void jlDrawSurfaceSet(jlSurfaceT target) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int jlGameGetAxis(byte which) {
|
int jlGameGetAxis(jbyte which) {
|
||||||
jint16 r;
|
jint16 r;
|
||||||
|
|
||||||
if (which == 0) {
|
if (which == 0) {
|
||||||
|
@ -142,20 +157,20 @@ int jlGameGetAxis(byte which) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool jlGameGetButton(byte which) {
|
jbool jlGameGetButton(jbyte which) {
|
||||||
if (which > 0) return (bool)(*BUTTON1 > 127);
|
if (which > 0) return (jbool)(*BUTTON1 > 127);
|
||||||
return (bool)(*BUTTON0 > 127);
|
return (jbool)(*BUTTON0 > 127);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool _jlImgCreate(jlImgT **img) {
|
jbool _jlImgCreate(jlImgT **img, jint16 line, char *file) {
|
||||||
jlImgT *t = NULL;
|
jlImgT *t = NULL;
|
||||||
|
|
||||||
// Are we loading into a new image?
|
// Are we loading into a new image?
|
||||||
if (*img == NULL) {
|
if (*img == NULL) {
|
||||||
t = (jlImgT *)jlMalloc(sizeof(jlImgT));
|
t = (jlImgT *)jlMallocEx(sizeof(jlImgT), line, file);
|
||||||
if (t == NULL) {
|
if (t == NULL) {
|
||||||
return false;
|
return jfalse;
|
||||||
}
|
}
|
||||||
*img = t;
|
*img = t;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +182,7 @@ bool _jlImgCreate(jlImgT **img) {
|
||||||
memcpy(t->palette, SHRCOLORS, sizeof(t->palette));
|
memcpy(t->palette, SHRCOLORS, sizeof(t->palette));
|
||||||
memcpy(t->pixels, SHRPIXELS, sizeof(t->pixels));
|
memcpy(t->pixels, SHRPIXELS, sizeof(t->pixels));
|
||||||
*img = t;
|
*img = t;
|
||||||
return true;
|
return jtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -200,11 +215,11 @@ char _jlKeyCheck(char key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool jlKeyPressed(void) {
|
jbool jlKeyPressed(void) {
|
||||||
bool result = false;
|
jbool result = jfalse;
|
||||||
// On other platforms we should call jlIdle() here.
|
// On other platforms we should call jlIdle() here.
|
||||||
if ((*KEYBOARD & 0x80) != 0) {
|
if ((*KEYBOARD & 0x80) != 0) {
|
||||||
result = (_jlKeyCheck(*KEYBOARD & 0x7F) != 0);
|
result = (_jlKeyCheck(*KEYBOARD & 0x7F) != 0) ? jtrue : jfalse;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -220,7 +235,79 @@ char jlKeyRead(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlPaletteSet(byte index, byte r, byte g, byte b) {
|
void jlModContinue(void) {
|
||||||
|
NTPContinueMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModFree(jlModT *mod) {
|
||||||
|
jint16 slot = (jint16)mod;
|
||||||
|
|
||||||
|
if (slot > 0) {
|
||||||
|
NTPKillBatch(slot);
|
||||||
|
JOEY_CHECK_TOOL_ERROR("NTPKillBatch")
|
||||||
|
_jlMusicList[slot - 1] = -_jlMusicList[slot - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlModIsPlaying(void) {
|
||||||
|
return (NTPGetPlayingMusic() != 0xFFFF) ? jtrue : jfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool _jlModLoad(jlModT **mod, char *filename) {
|
||||||
|
jint16 i;
|
||||||
|
char *temp = jlUtilMakePathname(filename, "ntp");
|
||||||
|
_jlPascalStringT file;
|
||||||
|
|
||||||
|
// Find open slot.
|
||||||
|
for (i=0; i<MUSIC_SLOTS; i++) {
|
||||||
|
if (_jlMusicList[i] < 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We get one?
|
||||||
|
if (i < MUSIC_SLOTS) {
|
||||||
|
_jlSetPascalString(file, temp);
|
||||||
|
NTPAddToBatch((Pointer)&file, i + 1);
|
||||||
|
JOEY_CHECK_TOOL_ERROR("NTPAddToBatch")
|
||||||
|
_jlMusicList[i] = -_jlMusicList[i];
|
||||||
|
*mod = &_jlMusicList[i];
|
||||||
|
return jtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModPause(void) {
|
||||||
|
NTPPauseMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModPlay(jlModT *mod) {
|
||||||
|
jint16 slot = (jint16)mod;
|
||||||
|
|
||||||
|
if (slot > 0) {
|
||||||
|
NTPSelectBatch(slot);
|
||||||
|
JOEY_CHECK_TOOL_ERROR("NTPSelectBatch")
|
||||||
|
NTPPlayMusic(jfalse);
|
||||||
|
JOEY_CHECK_TOOL_ERROR("NTPPlayMusic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModStop(void) {
|
||||||
|
NTPStopMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModVolume(jbyte volume) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlPaletteSet(jbyte index, jbyte r, jbyte g, jbyte b) {
|
||||||
SHRCOLORS[index].r = r;
|
SHRCOLORS[index].r = r;
|
||||||
SHRCOLORS[index].g = g;
|
SHRCOLORS[index].g = g;
|
||||||
SHRCOLORS[index].b = b;
|
SHRCOLORS[index].b = b;
|
||||||
|
@ -233,137 +320,38 @@ void jlPaletteSetFromImg(jlImgT *img) {
|
||||||
|
|
||||||
|
|
||||||
void jlSoundFree(jlSoundT *sound) {
|
void jlSoundFree(jlSoundT *sound) {
|
||||||
//***TODO***
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundIsPlaying(jlSoundT *sound) {
|
jbool jlSoundIsPlaying(jlSoundT *sound) {
|
||||||
//***TODO***
|
return jfalse;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool _jlSoundLoad(jlSoundT **sound, char *filename) {
|
jbool _jlSoundLoad(jlSoundT **sound, char *filename) {
|
||||||
//***TODO***
|
return jfalse;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiContinue(void) {
|
void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume) {
|
||||||
//***TODO***
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundMidiIsPlaying(void) {
|
void jlSoundStop(jlSoundT *sound) {
|
||||||
//***TODO***
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiPause(void) {
|
void main(void) {
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiPlay(char *name) {
|
|
||||||
(void)name;
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiStop(void) {
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModContinue(void) {
|
|
||||||
NTPContinueMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundModIsPlaying(void) {
|
|
||||||
return (NTPGetPlayingMusic() != 0xFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModPause(void) {
|
|
||||||
NTPPauseMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModPlay(char *name) {
|
|
||||||
char *temp = jlUtilMakePathname(name, "ntp");
|
|
||||||
_jlPascalStringT file;
|
|
||||||
|
|
||||||
_jlSetPascalString(file, temp);
|
|
||||||
NTPStartUp(userid());
|
|
||||||
JOEY_CHECK_TOOL_ERROR("NTPStartup")
|
|
||||||
NTPLoadOneMusic((Pointer)&file);
|
|
||||||
JOEY_CHECK_TOOL_ERROR("NTPLoadOneMusic")
|
|
||||||
NTPPlayMusic(false);
|
|
||||||
JOEY_CHECK_TOOL_ERROR("NTPPlayMusic")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModStop(void) {
|
|
||||||
NTPShutDown();
|
|
||||||
JOEY_CHECK_TOOL_ERROR("NTPShutDown")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundPlay(jlSoundT *sound) {
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilIdle(void) {
|
|
||||||
// No need to pump the message loop on the IIgs
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlUtilMustExit(void) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilShutdown(void) {
|
|
||||||
// Clear the display.
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
|
|
||||||
// Restore Border
|
|
||||||
jlDisplayBorder((jlBorderColorsE)_jlBorderSaved);
|
|
||||||
jlDisplayPresent();
|
|
||||||
|
|
||||||
// SHR Off
|
|
||||||
asmGrOff();
|
|
||||||
|
|
||||||
asmStop();
|
|
||||||
|
|
||||||
// Shutdown tools
|
|
||||||
NTPShutDown(); // NinjaTracker
|
|
||||||
UnloadOneTool(222);
|
|
||||||
MSShutDown(); // MIDI Synth
|
|
||||||
UnloadOneTool(35);
|
|
||||||
SoundShutDown(); // Sound Tool Set
|
|
||||||
MTShutDown(); // Misc Tools
|
|
||||||
DisposeAll(userid());
|
|
||||||
MMShutDown(userid()); // Memory Manager
|
|
||||||
TLShutDown(); // Tool Locator
|
|
||||||
|
|
||||||
jlUtilDie("Clean Exit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilStartup(char *appTitle) {
|
|
||||||
|
|
||||||
handle tempHandle;
|
handle tempHandle;
|
||||||
ptr ZeroPagePtr;
|
ptr ZeroPagePtr;
|
||||||
InitialLoadOutputRec initialLoad;
|
InitialLoadOutputRec initialLoad;
|
||||||
char *temp;
|
char *temp;
|
||||||
_jlPascalStringT toolFile;
|
_jlPascalStringT toolFile;
|
||||||
|
jint16 i;
|
||||||
(void)appTitle; // Unused on IIgs
|
|
||||||
|
|
||||||
TLStartUp(); // Tool Locator
|
TLStartUp(); // Tool Locator
|
||||||
JOEY_CHECK_TOOL_ERROR("TLStartUp")
|
JOEY_CHECK_TOOL_ERROR("TLStartUp")
|
||||||
|
@ -382,22 +370,6 @@ void jlUtilStartup(char *appTitle) {
|
||||||
ZeroPagePtr = *tempHandle;
|
ZeroPagePtr = *tempHandle;
|
||||||
SoundStartUp((word)ZeroPagePtr + DPForSound);
|
SoundStartUp((word)ZeroPagePtr + DPForSound);
|
||||||
|
|
||||||
// Try to load MidiSynth from SYSTEM:TOOLS
|
|
||||||
LoadOneTool(35, 0);
|
|
||||||
if (_toolErr) {
|
|
||||||
// Not there. Try to manually load it from our disk.
|
|
||||||
temp = jlUtilMakePathname("Tool035", NULL);
|
|
||||||
_jlSetPascalString(toolFile, temp);
|
|
||||||
initialLoad = InitialLoad(userid(), (Pointer)&toolFile, 0);
|
|
||||||
JOEY_CHECK_TOOL_ERROR("Tool035")
|
|
||||||
SetTSPtr(0, 35, initialLoad.startAddr);
|
|
||||||
MSBootInit();
|
|
||||||
// Do not JOEY_CHECK_TOOL_ERROR("MSBootInit") - it will fail when in fact it's just garbage in the A register.
|
|
||||||
// If we REALLY want to know, check carry clear.
|
|
||||||
}
|
|
||||||
MSStartUp(); // MIDI Synth
|
|
||||||
JOEY_CHECK_TOOL_ERROR("MSStartUp")
|
|
||||||
|
|
||||||
// Try to load NinjaTrackerPlus from SYSTEM:TOOLS
|
// Try to load NinjaTrackerPlus from SYSTEM:TOOLS
|
||||||
LoadOneTool(222, 0);
|
LoadOneTool(222, 0);
|
||||||
if (_toolErr) {
|
if (_toolErr) {
|
||||||
|
@ -411,6 +383,9 @@ void jlUtilStartup(char *appTitle) {
|
||||||
JOEY_CHECK_TOOL_ERROR("NTPBootInit")
|
JOEY_CHECK_TOOL_ERROR("NTPBootInit")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear out the 25 music slots provided by NTP so we can keep track of them.
|
||||||
|
for (i=0; i<MUSIC_SLOTS; i++) _jlMusicList[i] = -(i + 1); // Negative values available slots, positive are used.
|
||||||
|
|
||||||
// Start assembly module
|
// Start assembly module
|
||||||
_jlHertz = (int)ReadBParam((Word)0x1D) == 0 ? 60 : 50; // Is this a PAL or NTSC (0) machine?
|
_jlHertz = (int)ReadBParam((Word)0x1D) == 0 ? 60 : 50; // Is this a PAL or NTSC (0) machine?
|
||||||
asmStart(userid(), _jlHertz == 60 ? 0 : 1);
|
asmStart(userid(), _jlHertz == 60 ? 0 : 1);
|
||||||
|
@ -433,6 +408,34 @@ void jlUtilStartup(char *appTitle) {
|
||||||
jlDrawColorSet(0);
|
jlDrawColorSet(0);
|
||||||
jlDrawClear();
|
jlDrawClear();
|
||||||
jlDrawColorSet(15);
|
jlDrawColorSet(15);
|
||||||
|
|
||||||
jlDisplayPresent();
|
jlDisplayPresent();
|
||||||
|
|
||||||
|
// Run the main loop.
|
||||||
|
if (!setjmp(_jlJumpBuffer)) {
|
||||||
|
joeyMain();
|
||||||
|
|
||||||
|
// Clear the display.
|
||||||
|
jlDrawColorSet(0);
|
||||||
|
jlDrawClear();
|
||||||
|
|
||||||
|
// Restore Border
|
||||||
|
jlDisplayBorder((jlBorderColorsE)_jlBorderSaved);
|
||||||
|
jlDisplayPresent();
|
||||||
|
|
||||||
|
// SHR Off
|
||||||
|
asmGrOff();
|
||||||
|
|
||||||
|
asmStop();
|
||||||
|
|
||||||
|
// Shutdown tools
|
||||||
|
NTPShutDown(); // NinjaTracker
|
||||||
|
UnloadOneTool(222);
|
||||||
|
SoundShutDown(); // Sound Tool Set
|
||||||
|
MTShutDown(); // Misc Tools
|
||||||
|
DisposeAll(userid());
|
||||||
|
MMShutDown(userid()); // Memory Manager
|
||||||
|
TLShutDown(); // Tool Locator
|
||||||
|
|
||||||
|
jlUtilDie("Clean Exit.");
|
||||||
|
}
|
||||||
}
|
}
|
222
joeylib/src/jPixBuf.c
Normal file
222
joeylib/src/jPixBuf.c
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* JoeyLib
|
||||||
|
* Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "jPixBuf.h"
|
||||||
|
|
||||||
|
|
||||||
|
jlImgT *_jlBackingStore = NULL; // 4 bit representation
|
||||||
|
jbool _jlPixBufStarted = jfalse;
|
||||||
|
|
||||||
|
|
||||||
|
void jlDrawBlit8x8(jlSurfaceT source, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
||||||
|
int o1;
|
||||||
|
int o2;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
jlPixelPairT *pixels = (jlPixelPairT *)source;
|
||||||
|
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
||||||
|
|
||||||
|
if (pixels == NULL) {
|
||||||
|
pixels = (jlPixelPairT *)_jlBackingStore->pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jlUtilIsOdd(sx)) sx--; // sx must be even because there are two pixels per byte
|
||||||
|
o1 = sy * 160 + (int)(sx * 0.5); // This is in pixels...
|
||||||
|
|
||||||
|
if (jlUtilIsOdd(tx)) tx--; // tx must be even because there are two pixels per byte
|
||||||
|
o2 = ty * 160 + (int)(tx * 0.5); // This is in pixels...
|
||||||
|
|
||||||
|
for (y=0; y<8; y++) {
|
||||||
|
for (x=0; x<4; x++) {
|
||||||
|
target[o2++] = pixels[o1++];
|
||||||
|
}
|
||||||
|
o1 += 156;
|
||||||
|
o2 += 156;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlDrawBlit8x8a(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
||||||
|
int so; // Source Pixel Offset
|
||||||
|
int to; // Target Pixel Offset
|
||||||
|
int i;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int pos;
|
||||||
|
jlPixelPairT s; // Source Pixel
|
||||||
|
jlPixelPairT t; // Target Pixel
|
||||||
|
jlPixelPairT *pixels = (jlPixelPairT *)source;
|
||||||
|
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
||||||
|
|
||||||
|
if (pixels == NULL) {
|
||||||
|
pixels = (jlPixelPairT *)_jlBackingStore->pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jlUtilIsOdd(sx)) sx--; // sx must be even because there are two pixels per byte
|
||||||
|
so = sy * 160 + (int)(sx * 0.5); // This is in pixels...
|
||||||
|
|
||||||
|
if (jlUtilIsOdd(tx)) tx--; // tx must be even because there are two pixels per byte
|
||||||
|
to = ty * 160 + (int)(tx * 0.5); // This is in pixels...
|
||||||
|
|
||||||
|
// Color = <-- 40 tiles, 80 bytes, 160 pixels -->
|
||||||
|
// Stencil = <-- 20 bytes, 160 bits -->
|
||||||
|
|
||||||
|
for (y=0; y<8; y++) {
|
||||||
|
for (x=0; x<4; x++) {
|
||||||
|
|
||||||
|
t = target[to];
|
||||||
|
s = pixels[so++];
|
||||||
|
|
||||||
|
i = ((sy + y) * 320 + (sx + x * 2));
|
||||||
|
pos = 7 - (i % 8);
|
||||||
|
i /= 8;
|
||||||
|
|
||||||
|
//***FIX*** Another endian order issue. Left & Right are swapped.
|
||||||
|
if ((stencil->pixels[i] & (1 << pos)) != 0) {
|
||||||
|
t.r = s.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = ((sy + y) * 320 + (sx + x * 2 + 1));
|
||||||
|
pos = 7 - (i % 8);
|
||||||
|
i /= 8;
|
||||||
|
|
||||||
|
if ((stencil->pixels[i] & (1 << pos)) != 0) {
|
||||||
|
t.l = s.l;
|
||||||
|
}
|
||||||
|
|
||||||
|
target[to++] = t;
|
||||||
|
}
|
||||||
|
so += 156;
|
||||||
|
to += 156;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlDrawClear(void) {
|
||||||
|
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
||||||
|
memset(target, _jlDrawColorNibbles, 32000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbyte jlDrawPixelGet(jint16 x, jint16 y) {
|
||||||
|
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
||||||
|
int p = x / 2 + y * 160;
|
||||||
|
return (jlUtilIsOdd(x) ? target[p].l : target[p].r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlDrawPixelSet(jint16 x, jint16 y) {
|
||||||
|
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
||||||
|
jlPixelPairT *pixelPair = target + (y * 160) + (x / 2);
|
||||||
|
if (jlUtilIsOdd(x)) {
|
||||||
|
pixelPair->l = _jlDrawColor;
|
||||||
|
} else {
|
||||||
|
pixelPair->r = _jlDrawColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jlSurfaceT jlDrawSurfaceGet(void) {
|
||||||
|
return _jlDrawTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlDrawSurfaceSet(jlSurfaceT target) {
|
||||||
|
_jlDrawTarget = target;
|
||||||
|
_jlDrawTargetActual = (target == NULL) ? (jlSurfaceT)_jlBackingStore->pixels : target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool _jlImgCreate(jlImgT **img, jint16 line, char *file) {
|
||||||
|
jlImgT *t = NULL;
|
||||||
|
|
||||||
|
(void)line;
|
||||||
|
(void)file;
|
||||||
|
|
||||||
|
// Are we loading into a new image?
|
||||||
|
if (*img == NULL) {
|
||||||
|
t = (jlImgT *)jlMallocEx(sizeof(jlImgT), line, file);
|
||||||
|
if (t == NULL) {
|
||||||
|
return jfalse;
|
||||||
|
}
|
||||||
|
*img = t;
|
||||||
|
}
|
||||||
|
t = (jlImgT *)*img;
|
||||||
|
t->id[0] = 'I';
|
||||||
|
t->id[1] = 'M';
|
||||||
|
t->id[2] = 'G';
|
||||||
|
t->version = 0;
|
||||||
|
// Backing store does not exist at startup
|
||||||
|
if (_jlBackingStore != NULL) {
|
||||||
|
memcpy(t->palette, _jlBackingStore->palette, sizeof(t->palette));
|
||||||
|
memcpy(t->pixels, _jlBackingStore->pixels, sizeof(t->pixels));
|
||||||
|
}
|
||||||
|
*img = t;
|
||||||
|
return jtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlImgDisplay(jlImgT *img) {
|
||||||
|
memcpy(_jlBackingStore->palette, img->palette, sizeof(img->palette));
|
||||||
|
memcpy(_jlBackingStore->pixels, img->pixels, sizeof(img->pixels));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlPaletteSet(jbyte index, jbyte r, jbyte g, jbyte b) {
|
||||||
|
_jlBackingStore->palette[index].r = r;
|
||||||
|
_jlBackingStore->palette[index].g = g;
|
||||||
|
_jlBackingStore->palette[index].b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlPaletteSetFromImg(jlImgT *img) {
|
||||||
|
memcpy(_jlBackingStore->palette, img->palette, sizeof(img->palette));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jPixBufStart(void) {
|
||||||
|
if (!_jlPixBufStarted) {
|
||||||
|
// Create backing store
|
||||||
|
jlImgCreate(_jlBackingStore);
|
||||||
|
jlPaletteDefault();
|
||||||
|
jlDrawSurfaceSet(JOEY_DISPLAY);
|
||||||
|
jlDrawColorSet(0);
|
||||||
|
jlDrawClear();
|
||||||
|
jlDrawColorSet(15);
|
||||||
|
jlDisplayPresent();
|
||||||
|
_jlPixBufStarted = jtrue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//#include <stdio.h>
|
||||||
|
void jPixBufStop(void) {
|
||||||
|
if (_jlPixBufStarted) {
|
||||||
|
//***TODO*** For some reason, jlImgFree is NOT setting _jlBackingStore to NULL. BAD.
|
||||||
|
//printf("Calling jlImgFree on %p\n", _jlBackingStore);
|
||||||
|
jlImgFree(_jlBackingStore);
|
||||||
|
//printf("Back from jlImgFree\n");
|
||||||
|
_jlPixBufStarted = jfalse;
|
||||||
|
}
|
||||||
|
}
|
37
joeylib/src/jPixBuf.h
Normal file
37
joeylib/src/jPixBuf.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* JoeyLib
|
||||||
|
* Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef H_JPIXBUF_
|
||||||
|
#define H_JPIXBUF_
|
||||||
|
|
||||||
|
|
||||||
|
#include "joey.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern jlImgT *_jlBackingStore;
|
||||||
|
|
||||||
|
|
||||||
|
void jPixBufStart(void);
|
||||||
|
void jPixBufStop(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // H_JPIXBUF_
|
811
joeylib/src/jSDL12.c
Normal file
811
joeylib/src/jSDL12.c
Normal file
|
@ -0,0 +1,811 @@
|
||||||
|
/*
|
||||||
|
* JoeyLib
|
||||||
|
* Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "SDL/SDL.h"
|
||||||
|
|
||||||
|
#define POCKETMOD_IMPLEMENTATION
|
||||||
|
#include "3rdparty/pocketmod/pocketmod.h"
|
||||||
|
|
||||||
|
#include "joey.h"
|
||||||
|
#include "jPixBuf.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define AUDIO_FORMAT AUDIO_S16
|
||||||
|
#define AUDIO_FREQUENCY 44100
|
||||||
|
#define AUDIO_CHANNELS 2
|
||||||
|
|
||||||
|
#define BORDER_WIDTH 20
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t len;
|
||||||
|
} jlPlatformSoundT;
|
||||||
|
|
||||||
|
typedef struct jlSoundPlayingS {
|
||||||
|
jlPlatformSoundT *sound;
|
||||||
|
jlSoundChannelE channel; // Corresponds to IIgs 4soniq channel definitions.
|
||||||
|
jbyte volume; // 0 = silent, 255 = loud.
|
||||||
|
uint8_t *buffer;
|
||||||
|
uint32_t len;
|
||||||
|
struct jlSoundPlayingS *next;
|
||||||
|
} jlSoundPlayingT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
} jlPlatformModT;
|
||||||
|
|
||||||
|
|
||||||
|
static jbyte _jlBorderRGBs[16][3] = {
|
||||||
|
{ 0x00, 0x00, 0x00 }, // Black
|
||||||
|
{ 0xdd, 0x00, 0x33 }, // Deep Red
|
||||||
|
{ 0x00, 0x00, 0x99 }, // Deep Blue
|
||||||
|
{ 0xdd, 0x22, 0xdd }, // Purple
|
||||||
|
{ 0x00, 0x77, 0x22 }, // Dark Green
|
||||||
|
{ 0x55, 0x55, 0x55 }, // Dark Gray
|
||||||
|
{ 0x22, 0x22, 0xff }, // Medium Blue
|
||||||
|
{ 0x66, 0xaa, 0xff }, // Light Blue
|
||||||
|
{ 0x88, 0x55, 0x00 }, // Brown
|
||||||
|
{ 0xff, 0x66, 0x00 }, // Orange
|
||||||
|
{ 0xaa, 0xaa, 0xaa }, // Light Gray
|
||||||
|
{ 0xff, 0x99, 0x88 }, // Pink
|
||||||
|
{ 0x11, 0xdd, 0x00 }, // Green
|
||||||
|
{ 0xff, 0xff, 0x00 }, // Yellow
|
||||||
|
{ 0x44, 0xff, 0x99 }, // Acquamarine
|
||||||
|
{ 0xff, 0xff, 0xff } // White
|
||||||
|
};
|
||||||
|
|
||||||
|
static SDL_Surface *_jlWindow = NULL;
|
||||||
|
static SDL_Surface *_jlTexture = NULL;
|
||||||
|
static jbool _jlIsRunning = jtrue;
|
||||||
|
static jint16 _jlNumKeysDown = 0;
|
||||||
|
static char _jlLastKey = 0;
|
||||||
|
static SDL_Joystick **_jlControllers = NULL;
|
||||||
|
static jint16 _jlControllerCount = 0;
|
||||||
|
static juint16 _jlTimerValue = 0;
|
||||||
|
static SDL_TimerID _jlTimerId = 0;
|
||||||
|
static pocketmod_context _jlModContext;
|
||||||
|
static SDL_AudioSpec _jlAudioFormat;
|
||||||
|
static jbool _jlModPlaying = jfalse;
|
||||||
|
static jlPlatformModT *_jlModCurrent = NULL;
|
||||||
|
static jbyte _jlModVolume = 255;
|
||||||
|
static jlSoundPlayingT *_jlSoundList = NULL;
|
||||||
|
static jint16 _jlWindowZoom = 3;
|
||||||
|
|
||||||
|
|
||||||
|
static Uint32 _jlUtilTimer(Uint32 interval, void *param);
|
||||||
|
static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes);
|
||||||
|
|
||||||
|
|
||||||
|
extern jbool _jlSwapChannels;
|
||||||
|
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Uint32 _jlGetPixel(SDL_Surface *surface, int x, int y) {
|
||||||
|
|
||||||
|
int bpp = surface->format->BytesPerPixel;
|
||||||
|
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
||||||
|
|
||||||
|
switch(bpp) {
|
||||||
|
case 1:
|
||||||
|
return *p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
|
||||||
|
return *(Uint16 *)p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
||||||
|
return (Uint32)(p[0] << 16 | p[1] << 8 | p[2]);
|
||||||
|
else
|
||||||
|
return (Uint32)(p[0] | p[1] << 8 | p[2] << 16);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
|
||||||
|
return *(Uint32 *)p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void _jlPutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) {
|
||||||
|
|
||||||
|
int bpp = surface->format->BytesPerPixel;
|
||||||
|
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
||||||
|
|
||||||
|
switch(bpp) {
|
||||||
|
case 1:
|
||||||
|
*p = (Uint8)pixel;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
*(Uint16 *)p = (Uint16)pixel;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
|
||||||
|
p[0] = (pixel >> 16) & 0xff;
|
||||||
|
p[1] = (pixel >> 8) & 0xff;
|
||||||
|
p[2] = pixel & 0xff;
|
||||||
|
} else {
|
||||||
|
p[0] = pixel & 0xff;
|
||||||
|
p[1] = (pixel >> 8) & 0xff;
|
||||||
|
p[2] = (pixel >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
*(Uint32 *)p = pixel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
|
||||||
|
static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes) {
|
||||||
|
int i = 0;
|
||||||
|
jlSoundPlayingT *sound = NULL;
|
||||||
|
jlSoundPlayingT *prev = NULL;
|
||||||
|
jlSoundPlayingT *temp = NULL;
|
||||||
|
uint32_t length = 0;
|
||||||
|
int16_t *data = NULL;
|
||||||
|
int16_t *out = NULL;
|
||||||
|
float adjust = 1;
|
||||||
|
float mod[2];
|
||||||
|
int32_t work = 0;
|
||||||
|
jbool isRight = jfalse;
|
||||||
|
|
||||||
|
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
// Silence buffer.
|
||||||
|
memset(buffer, 0, bytes);
|
||||||
|
|
||||||
|
// Load in music.
|
||||||
|
if (_jlModPlaying) {
|
||||||
|
// Adjust volume. Convert to S16 by multiplying rendered values by 32767.
|
||||||
|
adjust = ((100.0 / 255.0) * (float)_jlModVolume) * 0.01 * 32767;
|
||||||
|
out = (int16_t *)buffer;
|
||||||
|
while (i < bytes) {
|
||||||
|
i += pocketmod_render(&_jlModContext, mod, sizeof(float[2]));
|
||||||
|
out[i] = (int16_t)((float)out[i] * adjust);
|
||||||
|
out[i + 1] = (int16_t)((float)out[i + 1] * adjust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix in sounds.
|
||||||
|
// Just adding samples together seems fine for floating point data.
|
||||||
|
// For integers, we need to clamp.
|
||||||
|
if (_jlSoundList) {
|
||||||
|
sound = _jlSoundList;
|
||||||
|
while (sound) {
|
||||||
|
// Find length of data to mix.
|
||||||
|
length = ((uint32_t)bytes > sound->len) ? sound->len : (uint32_t)bytes;
|
||||||
|
// Adjust volume.
|
||||||
|
adjust = ((100.0 / 255.0) * (float)sound->volume) * 0.01;
|
||||||
|
// Point at sample data.
|
||||||
|
data = (int16_t *)sound->buffer;
|
||||||
|
out = (int16_t *)buffer;
|
||||||
|
for (i=0; i<(int)(length / sizeof(int16_t)); i+=2) {
|
||||||
|
// Combine channels into a mono sample.
|
||||||
|
work = (int32_t)((data[i] + data[i + 1]) * 0.5);
|
||||||
|
// Determine channel.
|
||||||
|
// ***TODO*** These should be channel bits that can be ORed together!
|
||||||
|
isRight = (sound->channel & 0x01);
|
||||||
|
if (_jlSwapChannels) isRight = !isRight;
|
||||||
|
if (isRight) {
|
||||||
|
// Place sound in the right channel.
|
||||||
|
out[i + 1] += (int16_t)(work * adjust);
|
||||||
|
} else {
|
||||||
|
// Place sound in left channel.
|
||||||
|
out[i] += (int16_t)(work * adjust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sound->buffer += length;
|
||||||
|
sound->len -= length;
|
||||||
|
// Are we done with this sound?
|
||||||
|
if (sound->len == 0) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = sound->next;
|
||||||
|
} else {
|
||||||
|
_jlSoundList = sound->next;
|
||||||
|
}
|
||||||
|
temp = sound;
|
||||||
|
sound = sound->next;
|
||||||
|
jlFree(temp);
|
||||||
|
} else {
|
||||||
|
prev = sound;
|
||||||
|
sound = sound->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlDisplayPresent(void) {
|
||||||
|
|
||||||
|
jint16 sourceX;
|
||||||
|
jint16 sourceY;
|
||||||
|
jint16 targetX;
|
||||||
|
jint16 targetY;
|
||||||
|
juint16 pixelLeft;
|
||||||
|
juint16 pixelRight;
|
||||||
|
juint16 p = 0;
|
||||||
|
|
||||||
|
jlUtilIdle();
|
||||||
|
|
||||||
|
SDL_LockSurface(_jlWindow);
|
||||||
|
|
||||||
|
// Border.
|
||||||
|
SDL_FillRect(_jlWindow, NULL, SDL_MapRGB(_jlWindow->format, _jlBorderRGBs[_jlBorderColor][0], _jlBorderRGBs[_jlBorderColor][1], _jlBorderRGBs[_jlBorderColor][2]));
|
||||||
|
|
||||||
|
// Render 4 bit copy to proper pixel format.
|
||||||
|
// This extra step preserves palette effects.
|
||||||
|
targetY = _jlWindowZoom * BORDER_WIDTH;
|
||||||
|
for (sourceY = 0; sourceY < 200; sourceY++) {
|
||||||
|
targetX = _jlWindowZoom * BORDER_WIDTH;
|
||||||
|
for (sourceX = 0; sourceX < 160; sourceX++) {
|
||||||
|
// We decode this R/L instead of L/R for some reason. NO idea why yet. Endians?
|
||||||
|
pixelLeft = SDL_MapRGBA(_jlWindow->format,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[p].r].r * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[p].r].g * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[p].r].b * 16,
|
||||||
|
255);
|
||||||
|
pixelRight = SDL_MapRGBA(_jlWindow->format,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[p].l].r * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[p].l].g * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[p].l].b * 16,
|
||||||
|
255);
|
||||||
|
// Copy to display. SDL 1.2 does not scale surfaces, so we have to do it manually.
|
||||||
|
switch (_jlWindowZoom) {
|
||||||
|
case 1:
|
||||||
|
_jlPutPixel(_jlWindow, targetX, targetY, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 1, targetY, pixelRight);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
_jlPutPixel(_jlWindow, targetX, targetY, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 1, targetY, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX, targetY + 1, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 1, targetY + 1, pixelLeft);
|
||||||
|
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 2, targetY, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 3, targetY, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 2, targetY + 1, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 3, targetY + 1, pixelRight);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
// There is no way this is efficient.
|
||||||
|
_jlPutPixel(_jlWindow, targetX, targetY, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 1, targetY, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 2, targetY, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX, targetY + 1, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 1, targetY + 1, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 2, targetY + 1, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX, targetY + 2, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 1, targetY + 2, pixelLeft);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 2, targetY + 2, pixelLeft);
|
||||||
|
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 3, targetY, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 4, targetY, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 5, targetY, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 3, targetY + 1, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 4, targetY + 1, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 5, targetY + 1, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 3, targetY + 2, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 4, targetY + 2, pixelRight);
|
||||||
|
_jlPutPixel(_jlWindow, targetX + 5, targetY + 2, pixelRight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Increment pixel locations.
|
||||||
|
p++;
|
||||||
|
targetX += _jlWindowZoom + _jlWindowZoom;
|
||||||
|
}
|
||||||
|
targetY += _jlWindowZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UnlockSurface(_jlWindow);
|
||||||
|
SDL_Flip(_jlWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jint16 jlGameGetAxis(jbyte which) {
|
||||||
|
uint8_t axis;
|
||||||
|
short int unscaled;
|
||||||
|
short int max = SHRT_MIN;
|
||||||
|
short int min = SHRT_MAX;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
jlUtilIdle();
|
||||||
|
|
||||||
|
if (which == 0) {
|
||||||
|
axis = 0;
|
||||||
|
} else {
|
||||||
|
axis = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x=0; x<_jlControllerCount; x++) {
|
||||||
|
unscaled = SDL_JoystickGetAxis(_jlControllers[x], axis);
|
||||||
|
if (unscaled > 0) {
|
||||||
|
if (unscaled > max) {
|
||||||
|
max = unscaled;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (unscaled < min) {
|
||||||
|
min = unscaled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (-min > max ? min : max) / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlGameGetButton(jbyte which) {
|
||||||
|
uint8_t button;
|
||||||
|
int x;
|
||||||
|
jbool pressed = jfalse;
|
||||||
|
|
||||||
|
jlUtilIdle();
|
||||||
|
|
||||||
|
if (which == 0) {
|
||||||
|
button = 0;
|
||||||
|
} else {
|
||||||
|
button = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x=0; x<_jlControllerCount; x++) {
|
||||||
|
if (SDL_JoystickGetButton(_jlControllers[x], button)) {
|
||||||
|
pressed = jtrue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlKeyPressed(void) {
|
||||||
|
jlUtilIdle();
|
||||||
|
return (_jlNumKeysDown > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char jlKeyRead(void) {
|
||||||
|
jlUtilIdle();
|
||||||
|
return _jlLastKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModContinue(void) {
|
||||||
|
if (_jlModCurrent) _jlModPlaying = jtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModFree(jlModT *mod) {
|
||||||
|
jlPlatformModT *m = (jlPlatformModT *)mod;
|
||||||
|
|
||||||
|
// Is this the current mod?
|
||||||
|
if (m == _jlModCurrent) {
|
||||||
|
// Is it playing?
|
||||||
|
if (_jlModPlaying) {
|
||||||
|
// Lock and wait for current mod to stop playing.
|
||||||
|
SDL_LockAudio();
|
||||||
|
jlModStop();
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
}
|
||||||
|
_jlModCurrent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free loaded song.
|
||||||
|
if (m->data) {
|
||||||
|
jlFree(m->data);
|
||||||
|
jlFree(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlModIsPlaying(void) {
|
||||||
|
return _jlModPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool _jlModLoad(jlModT **mod, char *filename) {
|
||||||
|
FILE *in = NULL;
|
||||||
|
jlPlatformModT *m = NULL;
|
||||||
|
jbool result = jfalse;
|
||||||
|
|
||||||
|
// New MOD.
|
||||||
|
m = (jlPlatformModT *)jlMalloc(sizeof(jlPlatformModT));
|
||||||
|
if (m) {
|
||||||
|
// Load MOD.
|
||||||
|
in = fopen(jlUtilMakePathname(filename, "mod"), "rb");
|
||||||
|
if (in) {
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
m->size = ftell(in);
|
||||||
|
rewind(in);
|
||||||
|
m->data = (char *)jlMalloc(m->size);
|
||||||
|
if (m->data) {
|
||||||
|
fread(m->data, m->size, 1, in);
|
||||||
|
*mod = m;
|
||||||
|
result = jtrue;
|
||||||
|
}
|
||||||
|
fclose(in);
|
||||||
|
} else {
|
||||||
|
jlFree(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModPause(void) {
|
||||||
|
if (_jlModCurrent) _jlModPlaying = jfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModPlay(jlModT *mod) {
|
||||||
|
jlPlatformModT *m = (jlPlatformModT *)mod;
|
||||||
|
|
||||||
|
// Stop any currently playing MOD.
|
||||||
|
if (_jlModPlaying) jlModStop();
|
||||||
|
|
||||||
|
// Start playback.
|
||||||
|
if (pocketmod_init(&_jlModContext, m->data, m->size, _jlAudioFormat.freq)) {
|
||||||
|
_jlModPlaying = jtrue;
|
||||||
|
_jlModCurrent = m;
|
||||||
|
} else {
|
||||||
|
_jlModPlaying = jfalse;
|
||||||
|
_jlModCurrent = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModStop(void) {
|
||||||
|
_jlModPlaying = jfalse;
|
||||||
|
_jlModCurrent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModVolume(jbyte volume) {
|
||||||
|
_jlModVolume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSoundFree(jlSoundT *sound) {
|
||||||
|
jlPlatformSoundT *s = (jlPlatformSoundT *)sound;
|
||||||
|
|
||||||
|
if (s != NULL) {
|
||||||
|
// Stop playing this sound.
|
||||||
|
jlSoundStop(s);
|
||||||
|
// Free it.
|
||||||
|
if (s->data != NULL) jlFree(s->data);
|
||||||
|
jlFree(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlSoundIsPlaying(jlSoundT *sound) {
|
||||||
|
jlPlatformSoundT *s = (jlPlatformSoundT *)sound;
|
||||||
|
jlSoundPlayingT *cur = _jlSoundList;
|
||||||
|
|
||||||
|
// Are we being played?
|
||||||
|
SDL_LockAudio();
|
||||||
|
while (cur) {
|
||||||
|
if (cur->sound == s) {
|
||||||
|
// Yep
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
return jtrue;
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
|
||||||
|
return jfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool _jlSoundLoad(jlSoundT **sound, char *filename) {
|
||||||
|
SDL_AudioSpec wave;
|
||||||
|
SDL_AudioCVT cvt;
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t len;
|
||||||
|
jlPlatformSoundT *soundPlatform = NULL;
|
||||||
|
jbool result = jfalse;
|
||||||
|
|
||||||
|
soundPlatform = (jlPlatformSoundT *)jlMalloc(sizeof(jlPlatformSoundT));
|
||||||
|
if (soundPlatform) {
|
||||||
|
*sound = soundPlatform;
|
||||||
|
if (SDL_LoadWAV(jlUtilMakePathname(filename, "wav"), &wave, &data, &len)) {
|
||||||
|
// Do we need to convert to our internal format?
|
||||||
|
SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_FORMAT, AUDIO_CHANNELS, AUDIO_FREQUENCY);
|
||||||
|
if (cvt.needed) {
|
||||||
|
// Yes! Convert audio.
|
||||||
|
cvt.buf = (jbyte *)jlMalloc(len * cvt.len_mult);
|
||||||
|
if (cvt.buf) {
|
||||||
|
memcpy(cvt.buf, data, len);
|
||||||
|
cvt.len = len;
|
||||||
|
SDL_ConvertAudio(&cvt);
|
||||||
|
SDL_FreeWAV(data);
|
||||||
|
soundPlatform->len = cvt.len_cvt;
|
||||||
|
soundPlatform->data = (jbyte *)jlMalloc(soundPlatform->len);
|
||||||
|
if (soundPlatform->data) {
|
||||||
|
memcpy(soundPlatform->data, cvt.buf, soundPlatform->len);
|
||||||
|
result = jtrue;
|
||||||
|
}
|
||||||
|
data = cvt.buf; // This prevents a warning about taking the address of a packed struct.
|
||||||
|
jlFree(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No. Use as-is.
|
||||||
|
soundPlatform->len = len;
|
||||||
|
soundPlatform->data = (jbyte *)jlMalloc(soundPlatform->len);
|
||||||
|
if (soundPlatform->data) {
|
||||||
|
memcpy(&soundPlatform->data, data, soundPlatform->len);
|
||||||
|
result = jtrue;
|
||||||
|
}
|
||||||
|
SDL_FreeWAV(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result && soundPlatform) jlFree(soundPlatform);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume) {
|
||||||
|
jlSoundPlayingT *sp = NULL;
|
||||||
|
|
||||||
|
sp = (jlSoundPlayingT *)jlMalloc(sizeof(jlSoundPlayingT));
|
||||||
|
if (sp) {
|
||||||
|
SDL_LockAudio();
|
||||||
|
sp->sound = (jlPlatformSoundT *)sound;
|
||||||
|
sp->channel = channel;
|
||||||
|
sp->volume = volume;
|
||||||
|
sp->buffer = sp->sound->data;
|
||||||
|
sp->len = sp->sound->len;
|
||||||
|
sp->next = _jlSoundList;
|
||||||
|
_jlSoundList = sp;
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSoundStop(jlSoundT *sound) {
|
||||||
|
jlPlatformSoundT *s = (jlPlatformSoundT *)sound;
|
||||||
|
jlSoundPlayingT *prev = NULL;
|
||||||
|
jlSoundPlayingT *temp = NULL;
|
||||||
|
jlSoundPlayingT *cur = _jlSoundList;
|
||||||
|
|
||||||
|
// Are we being played?
|
||||||
|
SDL_LockAudio();
|
||||||
|
while (cur) {
|
||||||
|
if (cur->sound == s) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = cur->next;
|
||||||
|
} else {
|
||||||
|
_jlSoundList = cur->next;
|
||||||
|
}
|
||||||
|
temp = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
jlFree(temp);
|
||||||
|
} else {
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char _jlUtilIdleCheckKey(int sym) {
|
||||||
|
|
||||||
|
char key;
|
||||||
|
SDLMod mod;
|
||||||
|
|
||||||
|
// Do we even care about this key?
|
||||||
|
if ((sym < 8) || (sym > 127)) {
|
||||||
|
key = 0;
|
||||||
|
} else {
|
||||||
|
mod = SDL_GetModState();
|
||||||
|
key = (char)sym;
|
||||||
|
// Map LF to CR
|
||||||
|
if (key == 10) {
|
||||||
|
key = 13;
|
||||||
|
}
|
||||||
|
// Map DEL to BS
|
||||||
|
if (key == 127) {
|
||||||
|
key = 8;
|
||||||
|
}
|
||||||
|
// Convert to uppercase if needed
|
||||||
|
if ((mod == KMOD_SHIFT) && (key >= 'a') && (key <= 'z')) {
|
||||||
|
key -= 32;
|
||||||
|
}
|
||||||
|
// Is this outside the range we care about?
|
||||||
|
if ((key < ' ') || (key > '~')) {
|
||||||
|
if ((key != 8) && (key != 13) && (key != 27)) {
|
||||||
|
key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlUtilIdle(void) {
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
SDL_JoystickUpdate();
|
||||||
|
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
switch(event.type) {
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
// Track keydown/keyup and remember last key pressed.
|
||||||
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_F1:
|
||||||
|
_jlWindowZoom = 1;
|
||||||
|
_jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_F2:
|
||||||
|
_jlWindowZoom = 2;
|
||||||
|
_jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_F3:
|
||||||
|
_jlWindowZoom = 3;
|
||||||
|
_jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_jlLastKey = _jlUtilIdleCheckKey(event.key.keysym.sym);
|
||||||
|
if (_jlLastKey > 0) {
|
||||||
|
//***FIX***
|
||||||
|
//_jlNumKeysDown++;
|
||||||
|
_jlNumKeysDown = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYUP:
|
||||||
|
if (_jlUtilIdleCheckKey(event.key.keysym.sym) > 0) {
|
||||||
|
//***FIX***
|
||||||
|
//_jlNumKeysDown--;
|
||||||
|
_jlNumKeysDown = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_QUIT:
|
||||||
|
_jlIsRunning = jfalse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("Keys down: %d\n", _jlNumKeysDown);
|
||||||
|
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlUtilMustExit(void) {
|
||||||
|
return !_jlIsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Uint32 _jlUtilTimer(Uint32 interval, void *param) {
|
||||||
|
(void)param;
|
||||||
|
_jlTimerValue++;
|
||||||
|
return(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
juint16 jlUtilTimer(void) {
|
||||||
|
return _jlTimerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlUtilTitleSet(char *title) {
|
||||||
|
SDL_WM_SetCaption(title, NULL); // ***TODO*** Needs icon.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
int joysticks;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
// Start low-level tools
|
||||||
|
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
|
||||||
|
jlUtilDie(SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up controllers
|
||||||
|
joysticks = SDL_NumJoysticks();
|
||||||
|
_jlControllers = (SDL_Joystick **)jlMalloc(sizeof(SDL_Joystick *) * (unsigned long)joysticks);
|
||||||
|
for (x=0; x<joysticks; x++) _jlControllers[_jlControllerCount++] = SDL_JoystickOpen(x);
|
||||||
|
|
||||||
|
// Create a window and renderer using SDL
|
||||||
|
_jlWindow = SDL_SetVideoMode((320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, 16, SDL_SWSURFACE);
|
||||||
|
_jlTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 200, 16, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
// Set up audio device
|
||||||
|
_jlAudioFormat.freq = AUDIO_FREQUENCY;
|
||||||
|
_jlAudioFormat.format = AUDIO_FORMAT;
|
||||||
|
_jlAudioFormat.channels = 2;
|
||||||
|
_jlAudioFormat.samples = AUDIO_CHANNELS;
|
||||||
|
_jlAudioFormat.callback = _jlAudioCallback;
|
||||||
|
_jlAudioFormat.userdata = NULL;
|
||||||
|
if (SDL_OpenAudio(&_jlAudioFormat, &_jlAudioFormat) < 0) jlUtilDie(SDL_GetError());
|
||||||
|
SDL_PauseAudio(0);
|
||||||
|
|
||||||
|
// Start 1/60th second timer
|
||||||
|
_jlTimerId = SDL_AddTimer(1000 / 60, _jlUtilTimer, NULL);
|
||||||
|
|
||||||
|
jPixBufStart();
|
||||||
|
|
||||||
|
// Run the main loop.
|
||||||
|
if (!setjmp(_jlJumpBuffer)) {
|
||||||
|
joeyMain();
|
||||||
|
|
||||||
|
jPixBufStop();
|
||||||
|
|
||||||
|
SDL_RemoveTimer(_jlTimerId);
|
||||||
|
for (x=0; x<_jlControllerCount; x++) SDL_JoystickClose(_jlControllers[x]);
|
||||||
|
_jlControllerCount = 0;
|
||||||
|
jlFree(_jlControllers);
|
||||||
|
SDL_PauseAudio(1);
|
||||||
|
SDL_CloseAudio();
|
||||||
|
while (_jlSoundList) jlSoundStop(_jlSoundList);
|
||||||
|
jlModStop();
|
||||||
|
SDL_FreeSurface(_jlTexture);
|
||||||
|
SDL_Quit(); // This releases _jlWindow.
|
||||||
|
|
||||||
|
jlUtilDie("Clean Exit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
781
joeylib/src/jSDL2.c
Normal file
781
joeylib/src/jSDL2.c
Normal file
|
@ -0,0 +1,781 @@
|
||||||
|
/*
|
||||||
|
* JoeyLib
|
||||||
|
* Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "SDL2/SDL.h"
|
||||||
|
|
||||||
|
#define POCKETMOD_IMPLEMENTATION
|
||||||
|
#include "3rdparty/pocketmod/pocketmod.h"
|
||||||
|
|
||||||
|
#include "joey.h"
|
||||||
|
#include "jPixBuf.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define AUDIO_FORMAT AUDIO_F32 // PocketMod returns floats.
|
||||||
|
#define AUDIO_FREQUENCY 44100
|
||||||
|
#define AUDIO_CHANNELS 2
|
||||||
|
|
||||||
|
#define BORDER_WIDTH 20
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t len;
|
||||||
|
} jlPlatformSoundT;
|
||||||
|
|
||||||
|
typedef struct jlSoundPlayingS {
|
||||||
|
jlPlatformSoundT *sound;
|
||||||
|
jlSoundChannelE channel; // Corresponds to IIgs 4soniq channel definitions.
|
||||||
|
jbyte volume; // 0 = silent, 255 = loud.
|
||||||
|
uint8_t *buffer;
|
||||||
|
uint32_t len;
|
||||||
|
struct jlSoundPlayingS *next;
|
||||||
|
} jlSoundPlayingT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
} jlPlatformModT;
|
||||||
|
|
||||||
|
|
||||||
|
static jbyte _jlBorderRGBs[16][3] = {
|
||||||
|
{ 0x00, 0x00, 0x00 }, // Black
|
||||||
|
{ 0xdd, 0x00, 0x33 }, // Deep Red
|
||||||
|
{ 0x00, 0x00, 0x99 }, // Deep Blue
|
||||||
|
{ 0xdd, 0x22, 0xdd }, // Purple
|
||||||
|
{ 0x00, 0x77, 0x22 }, // Dark Green
|
||||||
|
{ 0x55, 0x55, 0x55 }, // Dark Gray
|
||||||
|
{ 0x22, 0x22, 0xff }, // Medium Blue
|
||||||
|
{ 0x66, 0xaa, 0xff }, // Light Blue
|
||||||
|
{ 0x88, 0x55, 0x00 }, // Brown
|
||||||
|
{ 0xff, 0x66, 0x00 }, // Orange
|
||||||
|
{ 0xaa, 0xaa, 0xaa }, // Light Gray
|
||||||
|
{ 0xff, 0x99, 0x88 }, // Pink
|
||||||
|
{ 0x11, 0xdd, 0x00 }, // Green
|
||||||
|
{ 0xff, 0xff, 0x00 }, // Yellow
|
||||||
|
{ 0x44, 0xff, 0x99 }, // Acquamarine
|
||||||
|
{ 0xff, 0xff, 0xff } // White
|
||||||
|
};
|
||||||
|
|
||||||
|
static SDL_Window *_jlWindow = NULL;
|
||||||
|
static SDL_Renderer *_jlRenderer = NULL;
|
||||||
|
static SDL_Texture *_jlTexture = NULL; // Video card representation in ARGB
|
||||||
|
static SDL_PixelFormat *_jlPixelFormat = NULL; // Pixel format of _jlTexture
|
||||||
|
static jbool _jlIsRunning = jtrue;
|
||||||
|
static jint16 _jlNumKeysDown = 0;
|
||||||
|
static char _jlLastKey = 0;
|
||||||
|
static SDL_GameController **_jlControllers = NULL;
|
||||||
|
static jint16 _jlControllerCount = 0;
|
||||||
|
static juint16 _jlTimerValue = 0;
|
||||||
|
static SDL_TimerID _jlTimerId = 0;
|
||||||
|
static pocketmod_context _jlModContext;
|
||||||
|
static SDL_AudioSpec _jlAudioFormat;
|
||||||
|
static SDL_AudioDeviceID _jlAudioDevice;
|
||||||
|
static jbool _jlModPlaying = jfalse;
|
||||||
|
static jlPlatformModT *_jlModCurrent = NULL;
|
||||||
|
static jbyte _jlModVolume = 255;
|
||||||
|
static jlSoundPlayingT *_jlSoundList = NULL;
|
||||||
|
static jint16 _jlWindowZoom = 2;
|
||||||
|
|
||||||
|
|
||||||
|
static Uint32 _jlUtilTimer(Uint32 interval, void *param);
|
||||||
|
static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes);
|
||||||
|
|
||||||
|
|
||||||
|
extern jbool _jlSwapChannels;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||||
|
|
||||||
|
Uint32 _jlGetPixel(SDL_Surface *surface, int x, int y) {
|
||||||
|
|
||||||
|
int bpp = surface->format->BytesPerPixel;
|
||||||
|
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
||||||
|
|
||||||
|
switch(bpp) {
|
||||||
|
case 1:
|
||||||
|
return *p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
|
||||||
|
return *(Uint16 *)p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
||||||
|
return (Uint32)(p[0] << 16 | p[1] << 8 | p[2]);
|
||||||
|
else
|
||||||
|
return (Uint32)(p[0] | p[1] << 8 | p[2] << 16);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
|
||||||
|
return *(Uint32 *)p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _jlPutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) {
|
||||||
|
|
||||||
|
int bpp = surface->format->BytesPerPixel;
|
||||||
|
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
||||||
|
|
||||||
|
switch(bpp) {
|
||||||
|
case 1:
|
||||||
|
*p = (Uint8)pixel;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
*(Uint16 *)p = (Uint16)pixel;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
|
||||||
|
p[0] = (pixel >> 16) & 0xff;
|
||||||
|
p[1] = (pixel >> 8) & 0xff;
|
||||||
|
p[2] = pixel & 0xff;
|
||||||
|
} else {
|
||||||
|
p[0] = pixel & 0xff;
|
||||||
|
p[1] = (pixel >> 8) & 0xff;
|
||||||
|
p[2] = (pixel >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
*(Uint32 *)p = pixel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes) {
|
||||||
|
int i = 0;
|
||||||
|
jlSoundPlayingT *sound = NULL;
|
||||||
|
jlSoundPlayingT *prev = NULL;
|
||||||
|
jlSoundPlayingT *temp = NULL;
|
||||||
|
uint32_t length = 0;
|
||||||
|
float *data = NULL;
|
||||||
|
float *out = NULL;
|
||||||
|
float adjust = 1;
|
||||||
|
double work = 0;
|
||||||
|
jbool isRight = jfalse;
|
||||||
|
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
// Silence buffer.
|
||||||
|
memset(buffer, 0, bytes);
|
||||||
|
|
||||||
|
// Load in music.
|
||||||
|
if (_jlModPlaying) {
|
||||||
|
while (i < bytes) {
|
||||||
|
i += pocketmod_render(&_jlModContext, buffer + i, bytes - i);
|
||||||
|
}
|
||||||
|
// Adjust volume.
|
||||||
|
adjust = ((100.0 / 255.0) * (float)_jlModVolume) * 0.01;
|
||||||
|
out = (float *)buffer;
|
||||||
|
for (i=0; i<(int)(bytes / sizeof(float)); i+=2) {
|
||||||
|
out[i] *= adjust;
|
||||||
|
out[i + 1] *= adjust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix in sounds.
|
||||||
|
// Just adding samples together seems fine for floating point data.
|
||||||
|
// For integers, we need to clamp.
|
||||||
|
if (_jlSoundList) {
|
||||||
|
sound = _jlSoundList;
|
||||||
|
while (sound) {
|
||||||
|
// Find length of data to mix.
|
||||||
|
length = ((uint32_t)bytes > sound->len) ? sound->len : (uint32_t)bytes;
|
||||||
|
// Adjust volume.
|
||||||
|
adjust = ((100.0 / 255.0) * (float)sound->volume) * 0.01;
|
||||||
|
// Point at sample data.
|
||||||
|
data = (float *)sound->buffer;
|
||||||
|
out = (float *)buffer;
|
||||||
|
for (i=0; i<(int)(length / sizeof(float)); i+=2) {
|
||||||
|
// Combine channels into a mono sample.
|
||||||
|
work = (data[i] + data[i + 1]) * 0.5;
|
||||||
|
// Determine channel.
|
||||||
|
// ***TODO*** These should be channel bits that can be ORed together!
|
||||||
|
isRight = (sound->channel & 0x01);
|
||||||
|
if (_jlSwapChannels) isRight = !isRight;
|
||||||
|
if (isRight) {
|
||||||
|
// Place sound in the right channel.
|
||||||
|
out[i + 1] += (work * adjust);
|
||||||
|
} else {
|
||||||
|
// Place sound in left channel.
|
||||||
|
out[i] += (work * adjust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sound->buffer += length;
|
||||||
|
sound->len -= length;
|
||||||
|
// Are we done with this sound?
|
||||||
|
if (sound->len == 0) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = sound->next;
|
||||||
|
} else {
|
||||||
|
_jlSoundList = sound->next;
|
||||||
|
}
|
||||||
|
temp = sound;
|
||||||
|
sound = sound->next;
|
||||||
|
jlFree(temp);
|
||||||
|
} else {
|
||||||
|
prev = sound;
|
||||||
|
sound = sound->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlDisplayPresent(void) {
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int pitch = 0;
|
||||||
|
void *pixelData = NULL;
|
||||||
|
Uint32 *pixels = NULL;
|
||||||
|
SDL_Rect r;
|
||||||
|
|
||||||
|
jlUtilIdle();
|
||||||
|
|
||||||
|
// Render 4 bit copy to proper pixel format.
|
||||||
|
// This extra step preserves palette effects.
|
||||||
|
SDL_LockTexture(_jlTexture, NULL, &pixelData, &pitch);
|
||||||
|
pixels = (Uint32 *)pixelData;
|
||||||
|
for (y=0; y<200; y++) {
|
||||||
|
for (x=0; x<160; x++) {
|
||||||
|
// We decode this R/L instead of L/R for some reason. NO idea why yet. Endians?
|
||||||
|
pixels[i++] = SDL_MapRGBA(_jlPixelFormat,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[j].r].r * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[j].r].g * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[j].r].b * 16,
|
||||||
|
255);
|
||||||
|
pixels[i++] = SDL_MapRGBA(_jlPixelFormat,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[j].l].r * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[j].l].g * 16,
|
||||||
|
_jlBackingStore->palette[_jlBackingStore->pixels[j].l].b * 16,
|
||||||
|
255);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_UnlockTexture(_jlTexture);
|
||||||
|
|
||||||
|
// Border.
|
||||||
|
SDL_SetRenderDrawColor(_jlRenderer, _jlBorderRGBs[_jlBorderColor][0], _jlBorderRGBs[_jlBorderColor][1], _jlBorderRGBs[_jlBorderColor][2], 255);
|
||||||
|
SDL_RenderClear(_jlRenderer);
|
||||||
|
|
||||||
|
// Display.
|
||||||
|
r.x = _jlWindowZoom * BORDER_WIDTH;
|
||||||
|
r.y = _jlWindowZoom * BORDER_WIDTH;
|
||||||
|
r.w = 320 * _jlWindowZoom;
|
||||||
|
r.h = 200 * _jlWindowZoom;
|
||||||
|
SDL_RenderCopy(_jlRenderer, _jlTexture, NULL, &r);
|
||||||
|
SDL_RenderPresent(_jlRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jint16 jlGameGetAxis(jbyte which) {
|
||||||
|
SDL_GameControllerAxis axis;
|
||||||
|
short int unscaled;
|
||||||
|
short int max = SHRT_MIN;
|
||||||
|
short int min = SHRT_MAX;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
jlUtilIdle();
|
||||||
|
|
||||||
|
if (which == 0) {
|
||||||
|
axis = SDL_CONTROLLER_AXIS_LEFTX;
|
||||||
|
} else {
|
||||||
|
axis = SDL_CONTROLLER_AXIS_LEFTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x=0; x<_jlControllerCount; x++) {
|
||||||
|
unscaled = SDL_GameControllerGetAxis(_jlControllers[x], axis);
|
||||||
|
if (unscaled > 0) {
|
||||||
|
if (unscaled > max) {
|
||||||
|
max = unscaled;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (unscaled < min) {
|
||||||
|
min = unscaled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (-min > max ? min : max) / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlGameGetButton(jbyte which) {
|
||||||
|
SDL_GameControllerButton button;
|
||||||
|
int x;
|
||||||
|
jbool pressed = jfalse;
|
||||||
|
|
||||||
|
jlUtilIdle();
|
||||||
|
|
||||||
|
if (which == 0) {
|
||||||
|
button = SDL_CONTROLLER_BUTTON_A;
|
||||||
|
} else {
|
||||||
|
button = SDL_CONTROLLER_BUTTON_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x=0; x<_jlControllerCount; x++) {
|
||||||
|
if (SDL_GameControllerGetButton(_jlControllers[x], button)) {
|
||||||
|
pressed = jtrue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlKeyPressed(void) {
|
||||||
|
jlUtilIdle();
|
||||||
|
return (_jlNumKeysDown > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char jlKeyRead(void) {
|
||||||
|
jlUtilIdle();
|
||||||
|
return _jlLastKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModContinue(void) {
|
||||||
|
if (_jlModCurrent) _jlModPlaying = jtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModFree(jlModT *mod) {
|
||||||
|
jlPlatformModT *m = (jlPlatformModT *)mod;
|
||||||
|
|
||||||
|
// Is this the current mod?
|
||||||
|
if (m == _jlModCurrent) {
|
||||||
|
// Is it playing?
|
||||||
|
if (_jlModPlaying) {
|
||||||
|
// Lock and wait for current mod to stop playing.
|
||||||
|
SDL_LockAudioDevice(_jlAudioDevice);
|
||||||
|
jlModStop();
|
||||||
|
SDL_UnlockAudioDevice(_jlAudioDevice);
|
||||||
|
}
|
||||||
|
_jlModCurrent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free loaded song.
|
||||||
|
if (m->data) {
|
||||||
|
jlFree(m->data);
|
||||||
|
jlFree(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlModIsPlaying(void) {
|
||||||
|
return _jlModPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool _jlModLoad(jlModT **mod, char *filename) {
|
||||||
|
FILE *in = NULL;
|
||||||
|
jlPlatformModT *m = NULL;
|
||||||
|
jbool result = jfalse;
|
||||||
|
|
||||||
|
// New MOD.
|
||||||
|
m = (jlPlatformModT *)jlMalloc(sizeof(jlPlatformModT));
|
||||||
|
if (m) {
|
||||||
|
// Load MOD.
|
||||||
|
in = fopen(jlUtilMakePathname(filename, "mod"), "rb");
|
||||||
|
if (in) {
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
m->size = ftell(in);
|
||||||
|
rewind(in);
|
||||||
|
m->data = (char *)jlMalloc(m->size);
|
||||||
|
if (m->data) {
|
||||||
|
fread(m->data, m->size, 1, in);
|
||||||
|
*mod = m;
|
||||||
|
result = jtrue;
|
||||||
|
}
|
||||||
|
fclose(in);
|
||||||
|
} else {
|
||||||
|
jlFree(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModPause(void) {
|
||||||
|
if (_jlModCurrent) _jlModPlaying = jfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModPlay(jlModT *mod) {
|
||||||
|
jlPlatformModT *m = (jlPlatformModT *)mod;
|
||||||
|
|
||||||
|
// Stop any currently playing MOD.
|
||||||
|
if (_jlModPlaying) jlModStop();
|
||||||
|
|
||||||
|
// Start playback.
|
||||||
|
if (pocketmod_init(&_jlModContext, m->data, m->size, _jlAudioFormat.freq)) {
|
||||||
|
_jlModPlaying = jtrue;
|
||||||
|
_jlModCurrent = m;
|
||||||
|
} else {
|
||||||
|
_jlModPlaying = jfalse;
|
||||||
|
_jlModCurrent = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModStop(void) {
|
||||||
|
_jlModPlaying = jfalse;
|
||||||
|
_jlModCurrent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlModVolume(jbyte volume) {
|
||||||
|
_jlModVolume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSoundFree(jlSoundT *sound) {
|
||||||
|
jlPlatformSoundT *s = (jlPlatformSoundT *)sound;
|
||||||
|
|
||||||
|
if (s != NULL) {
|
||||||
|
// Stop playing this sound.
|
||||||
|
jlSoundStop(s);
|
||||||
|
// Free it.
|
||||||
|
if (s->data != NULL) jlFree(s->data);
|
||||||
|
jlFree(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlSoundIsPlaying(jlSoundT *sound) {
|
||||||
|
jlPlatformSoundT *s = (jlPlatformSoundT *)sound;
|
||||||
|
jlSoundPlayingT *cur = _jlSoundList;
|
||||||
|
|
||||||
|
// Are we being played?
|
||||||
|
SDL_LockAudioDevice(_jlAudioDevice);
|
||||||
|
while (cur) {
|
||||||
|
if (cur->sound == s) {
|
||||||
|
// Yep
|
||||||
|
SDL_UnlockAudioDevice(_jlAudioDevice);
|
||||||
|
return jtrue;
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
SDL_UnlockAudioDevice(_jlAudioDevice);
|
||||||
|
|
||||||
|
return jfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool _jlSoundLoad(jlSoundT **sound, char *filename) {
|
||||||
|
SDL_AudioSpec wave;
|
||||||
|
SDL_AudioCVT cvt;
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t len;
|
||||||
|
jlPlatformSoundT *soundPlatform = NULL;
|
||||||
|
jbool result = jfalse;
|
||||||
|
|
||||||
|
soundPlatform = (jlPlatformSoundT *)jlMalloc(sizeof(jlPlatformSoundT));
|
||||||
|
if (soundPlatform) {
|
||||||
|
*sound = soundPlatform;
|
||||||
|
if (SDL_LoadWAV(jlUtilMakePathname(filename, "wav"), &wave, &data, &len)) {
|
||||||
|
// Do we need to convert to our internal format?
|
||||||
|
SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_FORMAT, AUDIO_CHANNELS, AUDIO_FREQUENCY);
|
||||||
|
if (cvt.needed) {
|
||||||
|
// Yes! Convert audio.
|
||||||
|
cvt.buf = (jbyte *)jlMalloc(len * cvt.len_mult);
|
||||||
|
if (cvt.buf) {
|
||||||
|
memcpy(cvt.buf, data, len);
|
||||||
|
cvt.len = len;
|
||||||
|
SDL_ConvertAudio(&cvt);
|
||||||
|
SDL_FreeWAV(data);
|
||||||
|
soundPlatform->len = cvt.len_cvt;
|
||||||
|
soundPlatform->data = (jbyte *)jlMalloc(soundPlatform->len);
|
||||||
|
if (soundPlatform->data) {
|
||||||
|
memcpy(soundPlatform->data, cvt.buf, soundPlatform->len);
|
||||||
|
result = jtrue;
|
||||||
|
}
|
||||||
|
data = cvt.buf; // This prevents a warning about taking the address of a packed struct.
|
||||||
|
jlFree(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No. Use as-is.
|
||||||
|
soundPlatform->len = len;
|
||||||
|
soundPlatform->data = (jbyte *)jlMalloc(soundPlatform->len);
|
||||||
|
if (soundPlatform->data) {
|
||||||
|
memcpy(&soundPlatform->data, data, soundPlatform->len);
|
||||||
|
result = jtrue;
|
||||||
|
}
|
||||||
|
SDL_FreeWAV(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result && soundPlatform) jlFree(soundPlatform);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume) {
|
||||||
|
jlSoundPlayingT *sp = NULL;
|
||||||
|
|
||||||
|
sp = (jlSoundPlayingT *)jlMalloc(sizeof(jlSoundPlayingT));
|
||||||
|
if (sp) {
|
||||||
|
SDL_LockAudioDevice(_jlAudioDevice);
|
||||||
|
sp->sound = sound;
|
||||||
|
sp->channel = channel;
|
||||||
|
sp->volume = volume;
|
||||||
|
sp->buffer = sp->sound->data;
|
||||||
|
sp->len = sp->sound->len;
|
||||||
|
sp->next = _jlSoundList;
|
||||||
|
_jlSoundList = sp;
|
||||||
|
SDL_UnlockAudioDevice(_jlAudioDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSoundStop(jlSoundT *sound) {
|
||||||
|
jlPlatformSoundT *s = (jlPlatformSoundT *)sound;
|
||||||
|
jlSoundPlayingT *prev = NULL;
|
||||||
|
jlSoundPlayingT *temp = NULL;
|
||||||
|
jlSoundPlayingT *cur = _jlSoundList;
|
||||||
|
|
||||||
|
// Are we being played?
|
||||||
|
SDL_LockAudioDevice(_jlAudioDevice);
|
||||||
|
while (cur) {
|
||||||
|
if (cur->sound == s) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = cur->next;
|
||||||
|
} else {
|
||||||
|
_jlSoundList = cur->next;
|
||||||
|
}
|
||||||
|
temp = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
jlFree(temp);
|
||||||
|
} else {
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_UnlockAudioDevice(_jlAudioDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char _jlUtilIdleCheckKey(int sym) {
|
||||||
|
|
||||||
|
char key = 0;
|
||||||
|
|
||||||
|
// Do we even care about this key?
|
||||||
|
if ((sym < 8) || (sym > 127)) {
|
||||||
|
key = 0;
|
||||||
|
} else {
|
||||||
|
SDL_Keymod mod = SDL_GetModState();
|
||||||
|
key = (char)sym;
|
||||||
|
// Map LF to CR
|
||||||
|
if (key == 10) {
|
||||||
|
key = 13;
|
||||||
|
}
|
||||||
|
// Map DEL to BS
|
||||||
|
if (key == 127) {
|
||||||
|
key = 8;
|
||||||
|
}
|
||||||
|
// Convert to uppercase if needed
|
||||||
|
if ((mod == KMOD_SHIFT) && (key >= 'a') && (key <= 'z')) {
|
||||||
|
key -= 32;
|
||||||
|
}
|
||||||
|
// Is this outside the range we care about?
|
||||||
|
if ((key < ' ') || (key > '~')) {
|
||||||
|
if ((key != 8) && (key != 13) && (key != 27)) {
|
||||||
|
key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlUtilIdle(void) {
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
switch(event.type) {
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
// Track keydown/keyup and remember last key pressed.
|
||||||
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_F1:
|
||||||
|
_jlWindowZoom = 1;
|
||||||
|
SDL_SetWindowSize(_jlWindow, (320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_F2:
|
||||||
|
_jlWindowZoom = 2;
|
||||||
|
SDL_SetWindowSize(_jlWindow, (320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_F3:
|
||||||
|
_jlWindowZoom = 3;
|
||||||
|
SDL_SetWindowSize(_jlWindow, (320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_jlLastKey = _jlUtilIdleCheckKey(event.key.keysym.sym);
|
||||||
|
if (_jlLastKey > 0) {
|
||||||
|
//***FIX***
|
||||||
|
//_jlNumKeysDown++;
|
||||||
|
_jlNumKeysDown = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYUP:
|
||||||
|
if (_jlUtilIdleCheckKey(event.key.keysym.sym) > 0) {
|
||||||
|
//***FIX***
|
||||||
|
//_jlNumKeysDown--;
|
||||||
|
_jlNumKeysDown = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_QUIT:
|
||||||
|
_jlIsRunning = jfalse;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("Keys down: %d\n", _jlNumKeysDown);
|
||||||
|
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jbool jlUtilMustExit(void) {
|
||||||
|
return !_jlIsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Uint32 _jlUtilTimer(Uint32 interval, void *param) {
|
||||||
|
(void)param;
|
||||||
|
_jlTimerValue++;
|
||||||
|
return(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
juint16 jlUtilTimer(void) {
|
||||||
|
return _jlTimerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlUtilTitleSet(char *title) {
|
||||||
|
SDL_SetWindowTitle(_jlWindow, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
int joysticks;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
// Start low-level tools
|
||||||
|
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
|
||||||
|
jlUtilDie(SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up controllers
|
||||||
|
joysticks = SDL_NumJoysticks();
|
||||||
|
_jlControllers = (SDL_GameController **)jlMalloc(sizeof(SDL_GameController *) * (unsigned long)joysticks);
|
||||||
|
for (x=0; x<joysticks; x++) {
|
||||||
|
if (SDL_IsGameController(x)) {
|
||||||
|
_jlControllers[_jlControllerCount++] = SDL_GameControllerOpen(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a window and renderer using SDL
|
||||||
|
_jlWindow = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, (320 + BORDER_WIDTH * 2) * _jlWindowZoom, (200 + BORDER_WIDTH * 2) * _jlWindowZoom, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||||
|
_jlRenderer = SDL_CreateRenderer(_jlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
_jlTexture = SDL_CreateTexture(_jlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 200);
|
||||||
|
_jlPixelFormat = SDL_AllocFormat(SDL_GetWindowPixelFormat(_jlWindow));
|
||||||
|
|
||||||
|
// Set up audio device
|
||||||
|
_jlAudioFormat.freq = AUDIO_FREQUENCY;
|
||||||
|
_jlAudioFormat.format = AUDIO_FORMAT;
|
||||||
|
_jlAudioFormat.channels = 2;
|
||||||
|
_jlAudioFormat.samples = AUDIO_CHANNELS;
|
||||||
|
_jlAudioFormat.callback = _jlAudioCallback;
|
||||||
|
_jlAudioFormat.userdata = NULL;
|
||||||
|
_jlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &_jlAudioFormat, &_jlAudioFormat, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||||
|
if (!_jlAudioDevice) jlUtilDie(SDL_GetError());
|
||||||
|
SDL_PauseAudioDevice(_jlAudioDevice, 0);
|
||||||
|
|
||||||
|
// Start 1/60th second timer
|
||||||
|
_jlTimerId = SDL_AddTimer(1000 / 60, _jlUtilTimer, NULL);
|
||||||
|
|
||||||
|
jPixBufStart();
|
||||||
|
|
||||||
|
// Run the main loop.
|
||||||
|
if (!setjmp(_jlJumpBuffer)) {
|
||||||
|
joeyMain();
|
||||||
|
|
||||||
|
jPixBufStop();
|
||||||
|
|
||||||
|
SDL_RemoveTimer(_jlTimerId);
|
||||||
|
for (x=0; x<_jlControllerCount; x++) SDL_GameControllerClose(_jlControllers[x]);
|
||||||
|
_jlControllerCount = 0;
|
||||||
|
jlFree(_jlControllers);
|
||||||
|
SDL_PauseAudioDevice(_jlAudioDevice, 1);
|
||||||
|
SDL_CloseAudioDevice(_jlAudioDevice);
|
||||||
|
while (_jlSoundList) jlSoundStop(_jlSoundList);
|
||||||
|
jlModStop();
|
||||||
|
SDL_FreeFormat(_jlPixelFormat);
|
||||||
|
SDL_DestroyTexture(_jlTexture);
|
||||||
|
SDL_DestroyRenderer(_jlRenderer);
|
||||||
|
SDL_DestroyWindow(_jlWindow);
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
jlUtilDie("Clean Exit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -22,21 +22,22 @@
|
||||||
#ifndef H_JOEY_
|
#ifndef H_JOEY_
|
||||||
#define H_JOEY_
|
#define H_JOEY_
|
||||||
|
|
||||||
#define JOEY_DEBUG
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
#include <stdlib.h>
|
extern "C" {
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef bool
|
|
||||||
typedef unsigned char bool;
|
|
||||||
#define true 1
|
|
||||||
#define false 0
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef unsigned char byte;
|
#include <stdlib.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned char jbool;
|
||||||
|
#define jtrue 1
|
||||||
|
#define jfalse 0
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned char jbyte;
|
||||||
|
|
||||||
|
|
||||||
#define JINT16_MIN -32768
|
#define JINT16_MIN -32768
|
||||||
|
@ -49,14 +50,20 @@ typedef unsigned char byte;
|
||||||
#define JUINT32_MIN 0
|
#define JUINT32_MIN 0
|
||||||
#define JUINT32_MAX 4294967295
|
#define JUINT32_MAX 4294967295
|
||||||
|
|
||||||
#define JOEY_DISPLAY (byte *)NULL
|
#define JOEY_DISPLAY (jbyte *)NULL
|
||||||
|
|
||||||
|
#define JOEY_INPUT_NONE 0
|
||||||
|
#define JOEY_INPUT_UP 1
|
||||||
|
#define JOEY_INPUT_DOWN 2
|
||||||
|
#define JOEY_INPUT_LEFT 3
|
||||||
|
#define JOEY_INPUT_RIGHT 4
|
||||||
|
#define JOEY_INPUT_PRIMARY 5
|
||||||
|
#define JOEY_INPUT_SECONDARY 6
|
||||||
|
|
||||||
|
|
||||||
// Determine platform and settings
|
// Determine platform and settings
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
#include <linux/limits.h>
|
|
||||||
|
|
||||||
#define JOEY_LINUX
|
#define JOEY_LINUX
|
||||||
#define JOEY_PC
|
#define JOEY_PC
|
||||||
#define JOEY_LITLE_ENDIAN
|
#define JOEY_LITLE_ENDIAN
|
||||||
|
@ -67,6 +74,40 @@ typedef unsigned short juint16;
|
||||||
typedef int jint32;
|
typedef int jint32;
|
||||||
typedef unsigned int juint32;
|
typedef unsigned int juint32;
|
||||||
|
|
||||||
|
#define JL_HAS_DISPLAYPRESENT
|
||||||
|
#define JL_HAS_DRAWBLIT8X8
|
||||||
|
#define JL_HAS_DRAWBLIT8X8A
|
||||||
|
#define JL_HAS_DRAWCLEAR
|
||||||
|
#define JL_HAS_DRAWPIXELGET
|
||||||
|
#define JL_HAS_DRAWPIXELSET
|
||||||
|
#define JL_HAS_DRAWSURFACEGET
|
||||||
|
#define JL_HAS_DRAWSURFACESET
|
||||||
|
#define JL_HAS_GAMEGETAXIS
|
||||||
|
#define JL_HAS_GAMEGETBUTTON
|
||||||
|
#define JL_HAS_IMGCREATE
|
||||||
|
#define JL_HAS_IMGDISPLAY
|
||||||
|
#define JL_HAS_KEYPRESSED
|
||||||
|
#define JL_HAS_KEYREAD
|
||||||
|
#define JL_HAS_MODCONTINUE
|
||||||
|
#define JL_HAS_MODFREE
|
||||||
|
#define JL_HAS_MODISPLAYING
|
||||||
|
#define JL_HAS_MODLOAD
|
||||||
|
#define JL_HAS_MODPAUSE
|
||||||
|
#define JL_HAS_MODPLAY
|
||||||
|
#define JL_HAS_MODSTOP
|
||||||
|
#define JL_HAS_MODVOLUME
|
||||||
|
#define JL_HAS_PALETTESET
|
||||||
|
#define JL_HAS_PALETTESETFROMIMG
|
||||||
|
#define JL_HAS_SOUNDFREE
|
||||||
|
#define JL_HAS_SOUNDISPLAYING
|
||||||
|
#define JL_HAS_SOUNDLOAD
|
||||||
|
#define JL_HAS_SOUNDPLAY
|
||||||
|
#define JL_HAS_SOUNDSTOP
|
||||||
|
#define JL_HAS_UTILIDLE
|
||||||
|
#define JL_HAS_UTILMUSTEXIT
|
||||||
|
#define JL_HAS_UTILTIMER
|
||||||
|
#define JL_HAS_UTILHASTITLESET
|
||||||
|
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
|
|
||||||
#define JOEY_WINDOWS
|
#define JOEY_WINDOWS
|
||||||
|
@ -78,6 +119,40 @@ typedef unsigned short juint16;
|
||||||
typedef int jint32;
|
typedef int jint32;
|
||||||
typedef unsigned int juint32;
|
typedef unsigned int juint32;
|
||||||
|
|
||||||
|
#define JL_HAS_DISPLAYPRESENT
|
||||||
|
#define JL_HAS_DRAWBLIT8X8
|
||||||
|
#define JL_HAS_DRAWBLIT8X8A
|
||||||
|
#define JL_HAS_DRAWCLEAR
|
||||||
|
#define JL_HAS_DRAWPIXELGET
|
||||||
|
#define JL_HAS_DRAWPIXELSET
|
||||||
|
#define JL_HAS_DRAWSURFACEGET
|
||||||
|
#define JL_HAS_DRAWSURFACESET
|
||||||
|
#define JL_HAS_GAMEGETAXIS
|
||||||
|
#define JL_HAS_GAMEGETBUTTON
|
||||||
|
#define JL_HAS_IMGCREATE
|
||||||
|
#define JL_HAS_IMGDISPLAY
|
||||||
|
#define JL_HAS_KEYPRESSED
|
||||||
|
#define JL_HAS_KEYREAD
|
||||||
|
#define JL_HAS_MODCONTINUE
|
||||||
|
#define JL_HAS_MODFREE
|
||||||
|
#define JL_HAS_MODISPLAYING
|
||||||
|
#define JL_HAS_MODLOAD
|
||||||
|
#define JL_HAS_MODPAUSE
|
||||||
|
#define JL_HAS_MODPLAY
|
||||||
|
#define JL_HAS_MODSTOP
|
||||||
|
#define JL_HAS_MODVOLUME
|
||||||
|
#define JL_HAS_PALETTESET
|
||||||
|
#define JL_HAS_PALETTESETFROMIMG
|
||||||
|
#define JL_HAS_SOUNDFREE
|
||||||
|
#define JL_HAS_SOUNDISPLAYING
|
||||||
|
#define JL_HAS_SOUNDLOAD
|
||||||
|
#define JL_HAS_SOUNDPLAY
|
||||||
|
#define JL_HAS_SOUNDSTOP
|
||||||
|
#define JL_HAS_UTILIDLE
|
||||||
|
#define JL_HAS_UTILMUSTEXIT
|
||||||
|
#define JL_HAS_UTILTIMER
|
||||||
|
#define JL_HAS_UTILHASTITLESET
|
||||||
|
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
|
|
||||||
#define JOEY_MACOS
|
#define JOEY_MACOS
|
||||||
|
@ -89,6 +164,40 @@ typedef unsigned short juint16;
|
||||||
typedef int jint32;
|
typedef int jint32;
|
||||||
typedef unsigned int juint32;
|
typedef unsigned int juint32;
|
||||||
|
|
||||||
|
#define JL_HAS_DISPLAYPRESENT
|
||||||
|
#define JL_HAS_DRAWBLIT8X8
|
||||||
|
#define JL_HAS_DRAWBLIT8X8A
|
||||||
|
#define JL_HAS_DRAWCLEAR
|
||||||
|
#define JL_HAS_DRAWPIXELGET
|
||||||
|
#define JL_HAS_DRAWPIXELSET
|
||||||
|
#define JL_HAS_DRAWSURFACEGET
|
||||||
|
#define JL_HAS_DRAWSURFACESET
|
||||||
|
#define JL_HAS_GAMEGETAXIS
|
||||||
|
#define JL_HAS_GAMEGETBUTTON
|
||||||
|
#define JL_HAS_IMGCREATE
|
||||||
|
#define JL_HAS_IMGDISPLAY
|
||||||
|
#define JL_HAS_KEYPRESSED
|
||||||
|
#define JL_HAS_KEYREAD
|
||||||
|
#define JL_HAS_MODCONTINUE
|
||||||
|
#define JL_HAS_MODFREE
|
||||||
|
#define JL_HAS_MODISPLAYING
|
||||||
|
#define JL_HAS_MODLOAD
|
||||||
|
#define JL_HAS_MODPAUSE
|
||||||
|
#define JL_HAS_MODPLAY
|
||||||
|
#define JL_HAS_MODSTOP
|
||||||
|
#define JL_HAS_MODVOLUME
|
||||||
|
#define JL_HAS_PALETTESET
|
||||||
|
#define JL_HAS_PALETTESETFROMIMG
|
||||||
|
#define JL_HAS_SOUNDFREE
|
||||||
|
#define JL_HAS_SOUNDISPLAYING
|
||||||
|
#define JL_HAS_SOUNDLOAD
|
||||||
|
#define JL_HAS_SOUNDPLAY
|
||||||
|
#define JL_HAS_SOUNDSTOP
|
||||||
|
#define JL_HAS_UTILIDLE
|
||||||
|
#define JL_HAS_UTILMUSTEXIT
|
||||||
|
#define JL_HAS_UTILTIMER
|
||||||
|
#define JL_HAS_UTILHASTITLESET
|
||||||
|
|
||||||
#elif __ORCAC__
|
#elif __ORCAC__
|
||||||
|
|
||||||
#define JOEY_IIGS
|
#define JOEY_IIGS
|
||||||
|
@ -107,6 +216,40 @@ typedef unsigned long juint32;
|
||||||
#pragma lint -1
|
#pragma lint -1
|
||||||
#pragma debug 0
|
#pragma debug 0
|
||||||
|
|
||||||
|
#define JL_HAS_DISPLAYPRESENT
|
||||||
|
#define JL_HAS_DRAWBLIT8X8
|
||||||
|
#define JL_HAS_DRAWBLIT8X8A
|
||||||
|
#define JL_HAS_DRAWBLITMAP
|
||||||
|
#define JL_HAS_DRAWPIXELGET
|
||||||
|
#define JL_HAS_DRAWPIXELSET
|
||||||
|
#define JL_HAS_DRAWLINE
|
||||||
|
#define JL_HAS_DRAWCLEAR
|
||||||
|
#define JL_HAS_DRAWSURFACEGET
|
||||||
|
#define JL_HAS_DRAWSURFACESET
|
||||||
|
#define JL_HAS_GAMEGETAXIS
|
||||||
|
#define JL_HAS_GAMEGETBUTTON
|
||||||
|
#define JL_HAS_IMGCREATE
|
||||||
|
#define JL_HAS_IMGDISPLAY
|
||||||
|
#define JL_HAS_KEYPRESSED
|
||||||
|
#define JL_HAS_KEYREAD
|
||||||
|
#define JL_HAS_MODCONTINUE
|
||||||
|
#define JL_HAS_MODFREE
|
||||||
|
#define JL_HAS_MODISPLAYING
|
||||||
|
#define JL_HAS_MODLOAD
|
||||||
|
#define JL_HAS_MODPAUSE
|
||||||
|
#define JL_HAS_MODPLAY
|
||||||
|
#define JL_HAS_MODSTOP
|
||||||
|
#define JL_HAS_MODVOLUME
|
||||||
|
#define JL_HAS_PALETTESET
|
||||||
|
#define JL_HAS_PALETTESETFROMIMG
|
||||||
|
#define JL_HAS_SOUNDFREE
|
||||||
|
#define JL_HAS_SOUNDISPLAYING
|
||||||
|
#define JL_HAS_SOUNDLOAD
|
||||||
|
#define JL_HAS_SOUNDPLAY
|
||||||
|
#define JL_HAS_SOUNDSTOP
|
||||||
|
#define JL_HAS_UTILNIBBLESWAP
|
||||||
|
#define JL_HAS_UTILTIMER
|
||||||
|
|
||||||
#elif AMIGA
|
#elif AMIGA
|
||||||
|
|
||||||
#define JOEY_AMIGA
|
#define JOEY_AMIGA
|
||||||
|
@ -118,6 +261,96 @@ typedef unsigned int juint16;
|
||||||
typedef long jint32;
|
typedef long jint32;
|
||||||
typedef unsigned long juint32;
|
typedef unsigned long juint32;
|
||||||
|
|
||||||
|
#elif __BEOS__
|
||||||
|
|
||||||
|
#define JOEY_BEOS
|
||||||
|
#define JOEY_PC
|
||||||
|
#define JOEY_LITLE_ENDIAN
|
||||||
|
#define JOEY_PATH_SEPARATOR '/'
|
||||||
|
typedef short jint16;
|
||||||
|
typedef unsigned short juint16;
|
||||||
|
typedef int jint32;
|
||||||
|
typedef unsigned int juint32;
|
||||||
|
|
||||||
|
#define JL_HAS_DISPLAYPRESENT
|
||||||
|
#define JL_HAS_DRAWBLIT8X8
|
||||||
|
#define JL_HAS_DRAWBLIT8X8A
|
||||||
|
#define JL_HAS_DRAWCLEAR
|
||||||
|
#define JL_HAS_DRAWPIXELGET
|
||||||
|
#define JL_HAS_DRAWPIXELSET
|
||||||
|
#define JL_HAS_DRAWSURFACEGET
|
||||||
|
#define JL_HAS_DRAWSURFACESET
|
||||||
|
#define JL_HAS_GAMEGETAXIS
|
||||||
|
#define JL_HAS_GAMEGETBUTTON
|
||||||
|
#define JL_HAS_IMGCREATE
|
||||||
|
#define JL_HAS_IMGDISPLAY
|
||||||
|
#define JL_HAS_KEYPRESSED
|
||||||
|
#define JL_HAS_KEYREAD
|
||||||
|
#define JL_HAS_MODCONTINUE
|
||||||
|
#define JL_HAS_MODFREE
|
||||||
|
#define JL_HAS_MODISPLAYING
|
||||||
|
#define JL_HAS_MODLOAD
|
||||||
|
#define JL_HAS_MODPAUSE
|
||||||
|
#define JL_HAS_MODPLAY
|
||||||
|
#define JL_HAS_MODSTOP
|
||||||
|
#define JL_HAS_MODVOLUME
|
||||||
|
#define JL_HAS_PALETTESET
|
||||||
|
#define JL_HAS_PALETTESETFROMIMG
|
||||||
|
#define JL_HAS_SOUNDFREE
|
||||||
|
#define JL_HAS_SOUNDISPLAYING
|
||||||
|
#define JL_HAS_SOUNDLOAD
|
||||||
|
#define JL_HAS_SOUNDPLAY
|
||||||
|
#define JL_HAS_SOUNDSTOP
|
||||||
|
#define JL_HAS_UTILIDLE
|
||||||
|
#define JL_HAS_UTILMUSTEXIT
|
||||||
|
#define JL_HAS_UTILTIMER
|
||||||
|
#define JL_HAS_UTILHASTITLESET
|
||||||
|
|
||||||
|
#elif __HAIKU__
|
||||||
|
|
||||||
|
#define JOEY_HAIKU
|
||||||
|
#define JOEY_PC
|
||||||
|
#define JOEY_LITLE_ENDIAN
|
||||||
|
#define JOEY_PATH_SEPARATOR '/'
|
||||||
|
typedef short jint16;
|
||||||
|
typedef unsigned short juint16;
|
||||||
|
typedef int jint32;
|
||||||
|
typedef unsigned int juint32;
|
||||||
|
|
||||||
|
#define JL_HAS_DISPLAYPRESENT
|
||||||
|
#define JL_HAS_DRAWBLIT8X8
|
||||||
|
#define JL_HAS_DRAWBLIT8X8A
|
||||||
|
#define JL_HAS_DRAWCLEAR
|
||||||
|
#define JL_HAS_DRAWPIXELGET
|
||||||
|
#define JL_HAS_DRAWPIXELSET
|
||||||
|
#define JL_HAS_DRAWSURFACEGET
|
||||||
|
#define JL_HAS_DRAWSURFACESET
|
||||||
|
#define JL_HAS_GAMEGETAXIS
|
||||||
|
#define JL_HAS_GAMEGETBUTTON
|
||||||
|
#define JL_HAS_IMGCREATE
|
||||||
|
#define JL_HAS_IMGDISPLAY
|
||||||
|
#define JL_HAS_KEYPRESSED
|
||||||
|
#define JL_HAS_KEYREAD
|
||||||
|
#define JL_HAS_MODCONTINUE
|
||||||
|
#define JL_HAS_MODFREE
|
||||||
|
#define JL_HAS_MODISPLAYING
|
||||||
|
#define JL_HAS_MODLOAD
|
||||||
|
#define JL_HAS_MODPAUSE
|
||||||
|
#define JL_HAS_MODPLAY
|
||||||
|
#define JL_HAS_MODSTOP
|
||||||
|
#define JL_HAS_MODVOLUME
|
||||||
|
#define JL_HAS_PALETTESET
|
||||||
|
#define JL_HAS_PALETTESETFROMIMG
|
||||||
|
#define JL_HAS_SOUNDFREE
|
||||||
|
#define JL_HAS_SOUNDISPLAYING
|
||||||
|
#define JL_HAS_SOUNDLOAD
|
||||||
|
#define JL_HAS_SOUNDPLAY
|
||||||
|
#define JL_HAS_SOUNDSTOP
|
||||||
|
#define JL_HAS_UTILIDLE
|
||||||
|
#define JL_HAS_UTILMUSTEXIT
|
||||||
|
#define JL_HAS_UTILTIMER
|
||||||
|
#define JL_HAS_UTILHASTITLESET
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define JOEY_ST
|
#define JOEY_ST
|
||||||
|
@ -132,17 +365,39 @@ typedef unsigned long juint32;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef byte *jlSurfaceT;
|
// Provide vastly improved memory debugging on PC platforms.
|
||||||
|
#ifdef JOEY_DEBUG
|
||||||
|
#ifdef JOEY_PC
|
||||||
|
#define MEMWATCH
|
||||||
|
#include "3rdparty/memwatch/memwatch.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef jbyte *jlSurfaceT;
|
||||||
|
|
||||||
|
|
||||||
extern jlSurfaceT _jlDrawTarget;
|
extern jlSurfaceT _jlDrawTarget;
|
||||||
extern jlSurfaceT _jlDrawTargetActual;
|
extern jlSurfaceT _jlDrawTargetActual;
|
||||||
extern byte _jlDrawColor;
|
extern jbyte _jlDrawColor;
|
||||||
extern byte _jlDrawColorNibbles;
|
extern jbyte _jlDrawColorNibbles;
|
||||||
extern byte _jlBorderColor;
|
extern jbyte _jlBorderColor;
|
||||||
extern char _jlTempString[1024];
|
extern char _jlTempString[1024];
|
||||||
|
extern jmp_buf _jlJumpBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
enum _jlSoundChannelE {
|
||||||
|
CHANNEL_FRONT_LEFT = 0,
|
||||||
|
CHANNEL_FRONT_RIGHT = 1,
|
||||||
|
CHANNEL_SURROUND_LEFT = 2,
|
||||||
|
CHANNEL_SURROUND_RIGHT = 4,
|
||||||
|
CHANNEL_FRONT_HIGH_LEFT = 8,
|
||||||
|
CHANNEL_FRONT_HIGH_RIGHT = 16,
|
||||||
|
CHANNEL_SURROUND_BACK_LEFT = 32,
|
||||||
|
CHANNEL_SURROUND_BACK_RIGHT = 64
|
||||||
|
};
|
||||||
|
typedef enum _jlSoundChannelE jlSoundChannelE;
|
||||||
|
|
||||||
enum _jlBorderColorsE {
|
enum _jlBorderColorsE {
|
||||||
BORDER_BLACK = 0,
|
BORDER_BLACK = 0,
|
||||||
BORDER_DEEP_RED = 1,
|
BORDER_DEEP_RED = 1,
|
||||||
|
@ -164,33 +419,32 @@ enum _jlBorderColorsE {
|
||||||
typedef enum _jlBorderColorsE jlBorderColorsE;
|
typedef enum _jlBorderColorsE jlBorderColorsE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
byte b : 4;
|
jbyte b : 4;
|
||||||
byte g : 4;
|
jbyte g : 4;
|
||||||
byte r : 4;
|
jbyte r : 4;
|
||||||
byte reserved : 4;
|
jbyte reserved : 4;
|
||||||
} jlColorT;
|
} jlColorT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
byte l : 4;
|
jbyte l : 4;
|
||||||
byte r : 4;
|
jbyte r : 4;
|
||||||
} jlPixelPairT;
|
} jlPixelPairT;
|
||||||
|
|
||||||
typedef struct {
|
typedef void jlSoundT;
|
||||||
void *data;
|
|
||||||
jint16 channel;
|
typedef void jlModT;
|
||||||
} jlSoundT;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char id[3];
|
char id[3];
|
||||||
byte version;
|
jbyte version;
|
||||||
jlColorT palette[16]; // 4 bits reserved, 4 bits red, 4 green, 4 blue
|
jlColorT palette[16]; // 4 bits reserved, 4 bits red, 4 green, 4 blue
|
||||||
jlPixelPairT pixels[32000]; // 320x200, 4 bits per pixel
|
jlPixelPairT pixels[32000]; // 320x200, 4 bits per pixel
|
||||||
} jlImgT;
|
} jlImgT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char id[3];
|
char id[3];
|
||||||
byte version;
|
jbyte version;
|
||||||
byte pixels[8000]; // 320x200, 1 bit per pixel stencil buffer
|
jbyte pixels[8000]; // 320x200, 1 bit per pixel stencil buffer
|
||||||
} jlStnT;
|
} jlStnT;
|
||||||
|
|
||||||
typedef struct _jlStackS {
|
typedef struct _jlStackS {
|
||||||
|
@ -200,13 +454,13 @@ typedef struct _jlStackS {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char id[3];
|
char id[3];
|
||||||
byte version;
|
jbyte version;
|
||||||
juint16 length;
|
juint16 length;
|
||||||
byte *data;
|
jbyte *data;
|
||||||
} jlVecT;
|
} jlVecT;
|
||||||
|
|
||||||
|
|
||||||
// Memory Management
|
// Memory Management - provides basic memory debugging on target platform.
|
||||||
#ifdef JOEY_DEBUG
|
#ifdef JOEY_DEBUG
|
||||||
|
|
||||||
#define JOEY_MEM_BLOCKS 128
|
#define JOEY_MEM_BLOCKS 128
|
||||||
|
@ -218,10 +472,11 @@ typedef struct {
|
||||||
jint16 line;
|
jint16 line;
|
||||||
} jlMemoryBlockT;
|
} jlMemoryBlockT;
|
||||||
|
|
||||||
#define jlFree(p) _jlFree((void **)&(p))
|
#define jlFree(p) {_jlFree((void **)&(p)); p = 0;}
|
||||||
void _jlFree(void **pointer);
|
void _jlFree(void **pointer);
|
||||||
|
|
||||||
#define jlMalloc(s) _jlMalloc(s, __LINE__, __FILE__)
|
#define jlMalloc(s) _jlMalloc(s, __LINE__, (char *)__FILE__)
|
||||||
|
#define jlMallocEx(s, l, f) _jlMalloc(s, l, (char *)f)
|
||||||
void *_jlMalloc(size_t size, jint16 line, char *file);
|
void *_jlMalloc(size_t size, jint16 line, char *file);
|
||||||
|
|
||||||
#define jlRealloc(p, s) _jlRealloc(p, s)
|
#define jlRealloc(p, s) _jlRealloc(p, s)
|
||||||
|
@ -229,8 +484,9 @@ void *_jlRealloc(void *pointer, size_t size);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define jlFree free
|
#define jlFree(p) {free(p); p = 0;}
|
||||||
#define jlMalloc malloc
|
#define jlMalloc malloc
|
||||||
|
#define jlMallocEx(s, l, f) malloc(s)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -240,106 +496,138 @@ void jlDisplayPresent(void);
|
||||||
|
|
||||||
void jlDrawBlit8x8(jlSurfaceT source, jint16 sx, jint16 sy, jint16 tx, jint16 ty);
|
void jlDrawBlit8x8(jlSurfaceT source, jint16 sx, jint16 sy, jint16 tx, jint16 ty);
|
||||||
void jlDrawBlit8x8a(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty);
|
void jlDrawBlit8x8a(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty);
|
||||||
void jlDrawBlitMap(jint16 startX, jint16 startY, jint16 width, jint16 height, byte *mapData, juint16 stride, jlSurfaceT source);
|
void jlDrawBlitMap(jint16 startX, jint16 startY, jint16 width, jint16 height, jbyte *mapData, juint16 stride, jlSurfaceT source);
|
||||||
void jlDrawBox(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
void jlDrawBox(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
||||||
void jlDrawBoxFilled(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
void jlDrawBoxFilled(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
||||||
void jlDrawCircle(jint16 x, jint16 y, jint16 radius);
|
void jlDrawCircle(jint16 x, jint16 y, jint16 radius);
|
||||||
void jlDrawClear(void);
|
void jlDrawClear(void);
|
||||||
byte jlDrawColorGet(void);
|
jbyte jlDrawColorGet(void);
|
||||||
void jlDrawColorSet(byte index);
|
void jlDrawColorSet(jbyte index);
|
||||||
void jlDrawEllipse(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
void jlDrawEllipse(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
||||||
void jlDrawFill(jint16 x, jint16 y);
|
void jlDrawFill(jint16 x, jint16 y);
|
||||||
void jlDrawFillTo(jint16 x, jint16 y, byte color);
|
void jlDrawFillTo(jint16 x, jint16 y, jbyte color);
|
||||||
void jlDrawLine(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
void jlDrawLine(jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
||||||
byte jlDrawPixelGet(jint16 x, jint16 y);
|
jbyte jlDrawPixelGet(jint16 x, jint16 y);
|
||||||
void jlDrawPixelSet(jint16 x, jint16 y);
|
void jlDrawPixelSet(jint16 x, jint16 y);
|
||||||
jlSurfaceT jlDrawSurfaceGet(void);
|
jlSurfaceT jlDrawSurfaceGet(void);
|
||||||
void jlDrawSurfaceSet(jlSurfaceT target);
|
void jlDrawSurfaceSet(jlSurfaceT target);
|
||||||
|
|
||||||
jint16 jlGameGetAxis(byte which);
|
jint16 jlGameGetAxis(jbyte which);
|
||||||
bool jlGameGetButton(byte which);
|
jbool jlGameGetButton(jbyte which);
|
||||||
|
|
||||||
#define jlImgCopy(source, target) _jlImgCopy(source, (jlImgT **)&(target)) // Syntatic Sugar
|
#define jlImgCopy(target, source) _jlImgCopy((jlImgT **)&(target), source, __LINE__, (char *)__FILE__) // Syntatic Sugar
|
||||||
bool _jlImgCopy(jlImgT *source, jlImgT **target);
|
jbool _jlImgCopy(jlImgT **target, jlImgT *source, jint16 line, char *file);
|
||||||
#define jlImgCreate(img) _jlImgCreate((jlImgT **)&(img)) // Syntatic Sugar
|
#define jlImgCreate(img) _jlImgCreate((jlImgT **)&(img), __LINE__, (char *)__FILE__) // Syntatic Sugar
|
||||||
bool _jlImgCreate(jlImgT **img);
|
jbool _jlImgCreate(jlImgT **img, jint16 line, char *file);
|
||||||
void jlImgDisplay(jlImgT *img);
|
void jlImgDisplay(jlImgT *img);
|
||||||
void jlImgFree(jlImgT *img);
|
void jlImgFree(jlImgT *img);
|
||||||
#define jlImgLoad(img, filename) _jlImgLoad((jlImgT **)&(img), filename) // Syntatic Sugar
|
#define jlImgLoad(img, filename) _jlImgLoad((jlImgT **)&(img), filename, __LINE__, (char *)__FILE__) // Syntatic Sugar
|
||||||
bool _jlImgLoad(jlImgT **img, char *filename);
|
jbool _jlImgLoad(jlImgT **img, char *filename, jint16 line, char *file);
|
||||||
bool jlImgSave(jlImgT *img, char *filename);
|
#define jlImgSave(img, filename) _jlImgSave(img, filename) // Syntatic Sugar
|
||||||
|
jbool _jlImgSave(jlImgT *img, char *filename);
|
||||||
#define jlImgSurfaceGet(img) ((jlSurfaceT)img->pixels)
|
#define jlImgSurfaceGet(img) ((jlSurfaceT)img->pixels)
|
||||||
|
|
||||||
bool jlKeyPressed(void);
|
jbool jlKeyPressed(void);
|
||||||
char jlKeyRead(void);
|
char jlKeyRead(void);
|
||||||
void jlKeyWaitForAny(void);
|
void jlKeyWaitForAny(void);
|
||||||
|
|
||||||
void jlPaletteDefault(void); //***TODO*** Treat palettes like we do "surfaces" - allow changing STAs or display
|
void jlModContinue(void);
|
||||||
void jlPaletteSet(byte index, byte r, byte g, byte b); //***TODO*** Really need a matching "get"
|
void jlModFree(jlModT *mod);
|
||||||
|
jbool jlModIsPlaying(void);
|
||||||
|
#define jlModLoad(mod, filename) _jlModLoad((jlModT **)&(mod), filename) // Syntatic Sugar
|
||||||
|
jbool _jlModLoad(jlModT **mod, char *filename);
|
||||||
|
void jlModPause(void);
|
||||||
|
void jlModPlay(jlModT *mod);
|
||||||
|
void jlModStop(void);
|
||||||
|
void jlModVolume(jbyte volume);
|
||||||
|
|
||||||
|
void jlPaletteDefault(void); // ***TODO*** Treat palettes like we do "surfaces" - allow changing STAs or display
|
||||||
|
void jlPaletteSet(jbyte index, jbyte r, jbyte g, jbyte b); // ***TODO*** Really need a matching "get"
|
||||||
void jlPaletteSetFromImg(jlImgT *img);
|
void jlPaletteSetFromImg(jlImgT *img);
|
||||||
|
/*
|
||||||
|
void jlPaletteAssign(jbyte rowStart, jbyte rowEnd, jbyte palette);
|
||||||
|
void jlPaletteCopy(jbyte paletteTarget, jbyte paletteSource);
|
||||||
|
void jlPaletteDefault(jbyte palette);
|
||||||
|
void jlPaletteGet(jbyte palette, jbyte index, jbyte *r, jbyte *g, jbyte *b);
|
||||||
|
void jlPaletteLoad(char *file, juint16 mask);
|
||||||
|
void jlPaletteSave(char *file, juint16 mask);
|
||||||
|
void jlPaletteSet(jbyte palette, jbyte index, jbyte r, jbyte g, jbyte b);
|
||||||
|
void jlPaletteSetFromImg(jbyte palette, jlImgT *img);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
void jlSoundFree(jlSoundT *sound);
|
void jlSoundFree(jlSoundT *sound);
|
||||||
bool jlSoundIsPlaying(jlSoundT *sound);
|
jbool jlSoundIsPlaying(jlSoundT *sound);
|
||||||
#define jlSoundLoad(sound, filename) _jlSoundLoad((jlSoundT **)&(sound), filename) // Syntatic Sugar
|
#define jlSoundLoad(sound, filename) _jlSoundLoad((jlSoundT **)&(sound), filename) // Syntatic Sugar
|
||||||
bool _jlSoundLoad(jlSoundT **sound, char *filename);
|
jbool _jlSoundLoad(jlSoundT **sound, char *filename);
|
||||||
|
void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume);
|
||||||
void jlSoundMidiContinue(void);
|
void jlSoundStop(jlSoundT *sound);
|
||||||
bool jlSoundMidiIsPlaying(void);
|
void jlSoundSwapChannels(jbool swap);
|
||||||
void jlSoundMidiPause(void);
|
|
||||||
void jlSoundMidiPlay(char *name);
|
|
||||||
void jlSoundMidiStop(void);
|
|
||||||
void jlSoundModContinue(void);
|
|
||||||
bool jlSoundModIsPlaying(void);
|
|
||||||
void jlSoundModPause(void);
|
|
||||||
void jlSoundModPlay(char *name);
|
|
||||||
void jlSoundModStop(void);
|
|
||||||
void jlSoundPlay(jlSoundT *sound);
|
|
||||||
|
|
||||||
void jlStnFree(jlStnT *stn);
|
void jlStnFree(jlStnT *stn);
|
||||||
#define jlStnLoad(stn, filename) _jlStnLoad((jlStnT **)&(stn), filename) // Syntatic Sugar
|
#define jlStnLoad(stn, filename) _jlStnLoad((jlStnT **)&(stn), filename, __LINE__, (char *)__FILE__) // Syntatic Sugar
|
||||||
bool _jlStnLoad(jlStnT **stn, char *filename);
|
jbool _jlStnLoad(jlStnT **stn, char *filename, jint16 line, char *file);
|
||||||
|
|
||||||
#define jlUtilByteSwap(twoBytes) ((juint16)((twoBytes) & 0xff) >> 8) | (juint16)((twoBytes) << 8)
|
#define jlUtilByteSwap(twoBytes) ((juint16)((twoBytes) & 0xff) >> 8) | (juint16)((twoBytes) << 8)
|
||||||
void jlUtilDie(const char *why, ...) __attribute__((noreturn));
|
void jlUtilDie(const char *why, ...);
|
||||||
void jlUtilIdle(void);
|
void jlUtilIdle(void);
|
||||||
#define jlUtilIsOdd(x) (((x & 1) == 1) ? true : false)
|
jbool jlUtilInputRead(jbyte *key);
|
||||||
|
#define jlUtilIsOdd(x) (((x & 1) == 1) ? jtrue : jfalse)
|
||||||
char *jlUtilMakePathname(char *filename, char *extension);
|
char *jlUtilMakePathname(char *filename, char *extension);
|
||||||
bool jlUtilMustExit(void);
|
jbool jlUtilMustExit(void);
|
||||||
void jlUtilNibbleSwap(byte *mem, jint16 count, byte old, byte new);
|
void jlUtilNibbleSwap(jbyte *mem, jint16 count, jbyte oldValue, jbyte newValue);
|
||||||
juint16 jlUtilRandom(void);
|
juint16 jlUtilRandom(void);
|
||||||
juint32 jlUtilRandomSeedGet(void);
|
juint32 jlUtilRandomSeedGet(void);
|
||||||
void jlUtilRandomSeedSet(juint32 seed);
|
void jlUtilRandomSeedSet(juint32 seed);
|
||||||
void jlUtilShutdown(void) __attribute__((noreturn));
|
void jlUtilSay(char *format, ...);
|
||||||
void jlUtilSleep(juint16 sixtieths);
|
void jlUtilSleep(juint16 sixtieths);
|
||||||
#define jlUtilStackPop(stack) _jlUtilStackPop((jlStackT **)&(stack)) // Syntatic Sugar
|
#define jlUtilStackPop(stack) _jlUtilStackPop((jlStackT **)&(stack)) // Syntatic Sugar
|
||||||
void *_jlUtilStackPop(jlStackT **stack);
|
void *_jlUtilStackPop(jlStackT **stack);
|
||||||
#define jlUtilStackPush(stack, data) _jlUtilStackPush((jlStackT **)&(stack), data) // Syntatic Sugar
|
#define jlUtilStackPush(stack, data) _jlUtilStackPush((jlStackT **)&(stack), data, __LINE__, (char *)__FILE__) // Syntatic Sugar
|
||||||
void _jlUtilStackPush(jlStackT **stack, void *data);
|
void _jlUtilStackPush(jlStackT **stack, void *data, jint16 line, char *file);
|
||||||
void jlUtilStartup(char *appTitle);
|
|
||||||
juint16 jlUtilTimer(void);
|
juint16 jlUtilTimer(void);
|
||||||
juint16 jlUtilTimeSpan(juint16 past, juint16 current);
|
juint16 jlUtilTimeSpan(juint16 past, juint16 current);
|
||||||
|
void jlUtilTitleSet(char *title);
|
||||||
|
|
||||||
void jlVecDisplay(jlVecT *vec, jint16 x, jint16 y);
|
void jlVecDisplay(jlVecT *vec, jint16 x, jint16 y);
|
||||||
void jlVecFree(jlVecT *vec);
|
void jlVecFree(jlVecT *vec);
|
||||||
#define jlVecLoad(vec, filename) _jlVecLoad((jlVecT **)&(vec), filename) // Syntatic Sugar
|
#define jlVecLoad(vec, filename) _jlVecLoad((jlVecT **)&(vec), filename, __LINE__, (char *)__FILE__) // Syntatic Sugar
|
||||||
bool _jlVecLoad(jlVecT **vec, char *filename);
|
jbool _jlVecLoad(jlVecT **vec, char *filename, jint16 line, char *file);
|
||||||
|
|
||||||
|
|
||||||
|
// Must be provided by application
|
||||||
|
void joeyMain(void);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef JOEY_TOOLS
|
||||||
|
#undef JL_HAS_DISPLAYPRESENT
|
||||||
|
#undef JL_HAS_GAMEGETAXIS
|
||||||
|
#undef JL_HAS_GAMEGETBUTTON
|
||||||
|
#undef JL_HAS_KEYPRESSED
|
||||||
|
#undef JL_HAS_KEYREAD
|
||||||
|
#undef JL_HAS_UTILMUSTEXIT
|
||||||
|
#undef JL_HAS_UTILTIMER
|
||||||
|
#include "jPixBuf.h"
|
||||||
|
#define jlToolsStart jPixBufStart
|
||||||
|
#define jlToolsStop jPixBufStop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef JOEY_IIGS
|
#ifdef JOEY_IIGS
|
||||||
// Inlined functions - asm code
|
// Inlined functions - asm code
|
||||||
extern void asmB88(jlSurfaceT target, jlSurfaceT source, jint16 cx1, jint16 cy1, jint16 cx2, jint16 cy2);
|
extern void asmB88(jlSurfaceT target, jlSurfaceT source, jint16 cx1, jint16 cy1, jint16 cx2, jint16 cy2);
|
||||||
extern void asmB88a(jlSurfaceT target, jlSurfaceT source, byte *stencil, jint16 cx1, jint16 cy1, jint16 cx2, jint16 cy2);
|
extern void asmB88a(jlSurfaceT target, jlSurfaceT source, jbyte *stencil, jint16 cx1, jint16 cy1, jint16 cx2, jint16 cy2);
|
||||||
extern void asmDrawBM(jlSurfaceT target, jint16 startX, jint16 startY, jint16 width, jint16 height, byte *mapData, juint16 stride, jlSurfaceT source);
|
extern void asmDrawBM(jlSurfaceT target, jint16 startX, jint16 startY, jint16 width, jint16 height, jbyte *mapData, juint16 stride, jlSurfaceT source);
|
||||||
extern void asmDrawLine(jlSurfaceT target, jint16 color, jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
extern void asmDrawLine(jlSurfaceT target, jint16 color, jint16 x1, jint16 y1, jint16 x2, jint16 y2);
|
||||||
extern jint16 asmGetPoint(jlSurfaceT target, jint16 x, jint16 y);
|
extern jint16 asmGetPoint(jlSurfaceT target, jint16 x, jint16 y);
|
||||||
extern juint16 asmGetVbl(void);
|
extern juint16 asmGetVbl(void);
|
||||||
extern void asmNSwap(byte *mem, jint16 count, jint16 old, jint16 new);
|
extern void asmNSwap(jbyte *mem, jint16 count, jint16 old, jint16 new);
|
||||||
extern void asmPoint(jlSurfaceT target, jint16 color, jint16 x, jint16 y);
|
extern void asmPoint(jlSurfaceT target, jint16 color, jint16 x, jint16 y);
|
||||||
|
|
||||||
// Inlined functions
|
// Inlined functions
|
||||||
#define jlDrawBlit8x8(source, cx1, cy1, cx2, cy2) asmB88(_jlDrawTargetActual, source, cx1, cy1, cx2, cy2)
|
#define jlDrawBlit8x8(source, cx1, cy1, cx2, cy2) asmB88(_jlDrawTargetActual, source, cx1, cy1, cx2, cy2)
|
||||||
#define jlDrawBlit8x8a(source, stencil, cx1, cy1, cx2, cy2) asmB88a(_jlDrawTargetActual, source, (byte *)stencil->pixels, cx1, cy1, cx2, cy2)
|
#define jlDrawBlit8x8a(source, stencil, cx1, cy1, cx2, cy2) asmB88a(_jlDrawTargetActual, source, (jbyte *)stencil->pixels, cx1, cy1, cx2, cy2)
|
||||||
#define jlDrawBlitMap(startX, startY, width, height, mapData, stride, source) asmDrawBM(_jlDrawTargetActual, startX, startY, width, height, (byte *)mapData, stride, source)
|
#define jlDrawBlitMap(startX, startY, width, height, mapData, stride, source) asmDrawBM(_jlDrawTargetActual, startX, startY, width, height, (jbyte *)mapData, stride, source)
|
||||||
#define jlDrawPixelGet(x, y) asmGetPoint(_jlDrawTargetActual, x, y)
|
#define jlDrawPixelGet(x, y) asmGetPoint(_jlDrawTargetActual, x, y)
|
||||||
#define jlDrawPixelSet(x, y) asmPoint(_jlDrawTargetActual, _jlDrawColorNibbles, x, y)
|
#define jlDrawPixelSet(x, y) asmPoint(_jlDrawTargetActual, _jlDrawColorNibbles, x, y)
|
||||||
#define jlDrawLine(x1, y1, x2, y2) asmDrawLine(_jlDrawTargetActual, _jlDrawColor, x1, y1, x2, y2)
|
#define jlDrawLine(x1, y1, x2, y2) asmDrawLine(_jlDrawTargetActual, _jlDrawColor, x1, y1, x2, y2)
|
||||||
|
@ -352,4 +640,9 @@ void _jlDebugBorder(jlBorderColorsE color);
|
||||||
#endif // JOEY_IIGS
|
#endif // JOEY_IIGS
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif // H_JOEY_
|
#endif // H_JOEY_
|
||||||
|
|
BIN
joeylib/src/kongas.wav
(Stored with Git LFS)
Normal file
BIN
joeylib/src/kongas.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -1,259 +0,0 @@
|
||||||
/*
|
|
||||||
* JoeyLib
|
|
||||||
* Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "SDL/SDL.h"
|
|
||||||
#include "SDL/SDL_mixer.h"
|
|
||||||
|
|
||||||
#include "joey.h"
|
|
||||||
|
|
||||||
|
|
||||||
static SDL_Surface *_jlScreen = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
void jlDisplayPresent(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawBlit8x8(jlSurfaceT source, jint16 cx1, jint16 cy1, jint16 cx2, jint16 cy2) {
|
|
||||||
(void)source;
|
|
||||||
(void)cx1;
|
|
||||||
(void)cy1;
|
|
||||||
(void)cx2;
|
|
||||||
(void)cy2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawBlit8x8a(jlSurfaceT source, jint16 cx1, jint16 cy1, jint16 cx2, jint16 cy2, byte offset) {
|
|
||||||
(void)source;
|
|
||||||
(void)cx1;
|
|
||||||
(void)cy1;
|
|
||||||
(void)cx2;
|
|
||||||
(void)cy2;
|
|
||||||
(void)offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawBlitMap(jint16 startX, jint16 startY, jint16 width, jint16 height, byte *mapData, juint16 stride, jlSurfaceT source) {
|
|
||||||
(void)startX;
|
|
||||||
(void)startY;
|
|
||||||
(void)width;
|
|
||||||
(void)height;
|
|
||||||
(void)mapData;
|
|
||||||
(void)stride;
|
|
||||||
(void)source;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawClear(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
byte jlDrawPixelGet(jint16 x, jint16 y) {
|
|
||||||
(void)x;
|
|
||||||
(void)y;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawPixelSet(jint16 x, jint16 y) {
|
|
||||||
(void)x;
|
|
||||||
(void)y;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jlSurfaceT jlDrawSurfaceGet(void) {
|
|
||||||
return _jlDrawTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawSurfaceSet(jlSurfaceT target) {
|
|
||||||
_jlDrawTarget = target;
|
|
||||||
if (target == NULL) {
|
|
||||||
_jlDrawTargetActual = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jint16 jlGameGetAxis(byte which) {
|
|
||||||
(void)which;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlGameGetButton(byte which) {
|
|
||||||
(void)which;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlKeyPressed(void) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char jlKeyRead(void) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlPaletteSet(byte index, byte r, byte g, byte b) {
|
|
||||||
(void)index;
|
|
||||||
(void)r;
|
|
||||||
(void)g;
|
|
||||||
(void)b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlPaletteSetFromSta(jlStaT *sta) {
|
|
||||||
(void)sta;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundFree(jlSoundT *sound) {
|
|
||||||
(void)sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundIsPlaying(jlSoundT *sound) {
|
|
||||||
(void)sound;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _jlSoundLoad(jlSoundT **sound, char *filename) {
|
|
||||||
(void)sound;
|
|
||||||
(void)filename;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMusicContinue(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundMusicIsPlaying(void) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMusicPause(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMusicPlay(char *name) {
|
|
||||||
(void)name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMusicStop(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundPlay(jlSoundT *sound) {
|
|
||||||
(void)sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _jlStaCreate(jlStaT **sta) {
|
|
||||||
(void)sta;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlStaDisplay(jlStaT *sta) {
|
|
||||||
(void)sta;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilIdle(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlUtilMustExit(void) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilNibbleSwap(byte *mem, jint16 count, byte old, byte new) {
|
|
||||||
(void)mem;
|
|
||||||
(void)count;
|
|
||||||
(void)old;
|
|
||||||
(void)new;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilShutdown(void) {
|
|
||||||
SDL_FreeSurface(_jlScreen);
|
|
||||||
jlSoundMusicStop();
|
|
||||||
Mix_CloseAudio();
|
|
||||||
Mix_Quit();
|
|
||||||
SDL_Quit();
|
|
||||||
jlUtilDie("Clean Exit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilStartup(char *appTitle) {
|
|
||||||
|
|
||||||
Uint32 vflags;
|
|
||||||
int mflags;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
(void)appTitle;
|
|
||||||
|
|
||||||
// Start low-level tools
|
|
||||||
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
|
|
||||||
jlUtilDie(SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start audio mixer & music system.
|
|
||||||
mflags = MIX_INIT_MOD;
|
|
||||||
result = Mix_Init(mflags);
|
|
||||||
if ((result & mflags) != mflags) {
|
|
||||||
jlUtilDie(Mix_GetError());
|
|
||||||
}
|
|
||||||
if (Mix_OpenAudio(11025, AUDIO_U8, 1, 1024) == -1) {
|
|
||||||
jlUtilDie(Mix_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up video
|
|
||||||
vflags = SDL_HWSURFACE;
|
|
||||||
#ifdef JOEY_LINUX
|
|
||||||
SDL_WM_SetCaption(appTitle, NULL);
|
|
||||||
#else
|
|
||||||
vflags |= SDL_FULLSCREEN;
|
|
||||||
#endif
|
|
||||||
_jlScreen = SDL_SetVideoMode(320, 200, 16, vflags);
|
|
||||||
if (!_jlScreen) {
|
|
||||||
jlUtilDie(SDL_GetError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
juint16 jlUtilTimer(void) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,775 +0,0 @@
|
||||||
/*
|
|
||||||
* JoeyLib
|
|
||||||
* Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "SDL2/SDL.h"
|
|
||||||
#include "SDL2/SDL_mixer.h"
|
|
||||||
|
|
||||||
#include "joey.h"
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DMALLOC
|
|
||||||
#define DMALLOC_FUNC_CHECK
|
|
||||||
#include "dmalloc.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static SDL_Window *_jlWindow = NULL;
|
|
||||||
static SDL_Renderer *_jlRenderer = NULL;
|
|
||||||
static jlImgT *_jlBackingStore = NULL; // 4 bit representation
|
|
||||||
static SDL_Texture *_jlTexture = NULL; // Video card representation in ARGB
|
|
||||||
static SDL_PixelFormat *_jlPixelFormat = NULL; // Pixel format of _jlTexture
|
|
||||||
static bool _jlIsRunning = true;
|
|
||||||
static jint16 _jlNumKeysDown = 0;
|
|
||||||
static char _jlLastKey = 0;
|
|
||||||
static Mix_Music *_jlMusicHandle = NULL;
|
|
||||||
static SDL_GameController **_jlControllers = NULL;
|
|
||||||
static jint16 _jlControllerCount = 0;
|
|
||||||
static juint16 _jlTimerValue = 0;
|
|
||||||
static SDL_TimerID _jlTimerId = 0;
|
|
||||||
|
|
||||||
|
|
||||||
Uint32 _jlUtilTimer(Uint32 interval, void *param);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
|
||||||
|
|
||||||
Uint32 _jlGetPixel(SDL_Surface *surface, int x, int y) {
|
|
||||||
|
|
||||||
int bpp = surface->format->BytesPerPixel;
|
|
||||||
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
|
||||||
|
|
||||||
switch(bpp) {
|
|
||||||
case 1:
|
|
||||||
return *p;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
|
|
||||||
return *(Uint16 *)p;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
||||||
return (Uint32)(p[0] << 16 | p[1] << 8 | p[2]);
|
|
||||||
else
|
|
||||||
return (Uint32)(p[0] | p[1] << 8 | p[2] << 16);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
|
|
||||||
return *(Uint32 *)p;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void _jlPutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) {
|
|
||||||
|
|
||||||
int bpp = surface->format->BytesPerPixel;
|
|
||||||
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
|
|
||||||
|
|
||||||
switch(bpp) {
|
|
||||||
case 1:
|
|
||||||
*p = (Uint8)pixel;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
*(Uint16 *)p = (Uint16)pixel;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
|
|
||||||
p[0] = (pixel >> 16) & 0xff;
|
|
||||||
p[1] = (pixel >> 8) & 0xff;
|
|
||||||
p[2] = pixel & 0xff;
|
|
||||||
} else {
|
|
||||||
p[0] = pixel & 0xff;
|
|
||||||
p[1] = (pixel >> 8) & 0xff;
|
|
||||||
p[2] = (pixel >> 16) & 0xff;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
*(Uint32 *)p = pixel;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
void jlDisplayPresent(void) {
|
|
||||||
// Render 4 bit copy to proper pixel format.
|
|
||||||
// This extra step preserves palette effects.
|
|
||||||
//***TODO*** Fake border colors on PC
|
|
||||||
int i = 0;
|
|
||||||
int j = 0;
|
|
||||||
int pitch = 0;
|
|
||||||
void *pixelData = NULL;
|
|
||||||
jlUtilIdle();
|
|
||||||
SDL_LockTexture(_jlTexture, NULL, &pixelData, &pitch);
|
|
||||||
Uint32 *pixels = (Uint32 *)pixelData;
|
|
||||||
for (int y=0; y<200; y++) {
|
|
||||||
for (int x=0; x<160; x++) {
|
|
||||||
// We decode this R/L instead of L/R for some reason. NO idea why yet. Endians?
|
|
||||||
pixels[i++] = SDL_MapRGBA(_jlPixelFormat,
|
|
||||||
_jlBackingStore->palette[_jlBackingStore->pixels[j].r].r * 16,
|
|
||||||
_jlBackingStore->palette[_jlBackingStore->pixels[j].r].g * 16,
|
|
||||||
_jlBackingStore->palette[_jlBackingStore->pixels[j].r].b * 16,
|
|
||||||
255);
|
|
||||||
pixels[i++] = SDL_MapRGBA(_jlPixelFormat,
|
|
||||||
_jlBackingStore->palette[_jlBackingStore->pixels[j].l].r * 16,
|
|
||||||
_jlBackingStore->palette[_jlBackingStore->pixels[j].l].g * 16,
|
|
||||||
_jlBackingStore->palette[_jlBackingStore->pixels[j].l].b * 16,
|
|
||||||
255);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_UnlockTexture(_jlTexture);
|
|
||||||
|
|
||||||
SDL_RenderCopy(_jlRenderer, _jlTexture, NULL, NULL);
|
|
||||||
SDL_RenderPresent(_jlRenderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawBlit8x8(jlSurfaceT source, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
|
||||||
int o1;
|
|
||||||
int o2;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
jlPixelPairT *pixels = (jlPixelPairT *)source;
|
|
||||||
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
|
||||||
|
|
||||||
if (pixels == NULL) {
|
|
||||||
pixels = (jlPixelPairT *)_jlBackingStore->pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jlUtilIsOdd(sx)) sx--; // sx must be even because there are two pixels per byte
|
|
||||||
o1 = sy * 160 + (int)(sx * 0.5); // This is in pixels...
|
|
||||||
|
|
||||||
if (jlUtilIsOdd(tx)) tx--; // tx must be even because there are two pixels per byte
|
|
||||||
o2 = ty * 160 + (int)(tx * 0.5); // This is in pixels...
|
|
||||||
|
|
||||||
for (y=0; y<8; y++) {
|
|
||||||
for (x=0; x<4; x++) {
|
|
||||||
target[o2++] = pixels[o1++];
|
|
||||||
}
|
|
||||||
o1 += 156;
|
|
||||||
o2 += 156;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawBlit8x8a(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
|
||||||
int so; // Source Pixel Offset
|
|
||||||
int to; // Target Pixel Offset
|
|
||||||
int i;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int pos;
|
|
||||||
jlPixelPairT s; // Source Pixel
|
|
||||||
jlPixelPairT t; // Target Pixel
|
|
||||||
jlPixelPairT *pixels = (jlPixelPairT *)source;
|
|
||||||
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
|
||||||
|
|
||||||
if (pixels == NULL) {
|
|
||||||
pixels = (jlPixelPairT *)_jlBackingStore->pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jlUtilIsOdd(sx)) sx--; // sx must be even because there are two pixels per byte
|
|
||||||
so = sy * 160 + (int)(sx * 0.5); // This is in pixels...
|
|
||||||
|
|
||||||
if (jlUtilIsOdd(tx)) tx--; // tx must be even because there are two pixels per byte
|
|
||||||
to = ty * 160 + (int)(tx * 0.5); // This is in pixels...
|
|
||||||
|
|
||||||
// Color = <-- 40 tiles, 80 bytes, 160 pixels -->
|
|
||||||
// Stencil = <-- 20 bytes, 160 bits -->
|
|
||||||
|
|
||||||
for (y=0; y<8; y++) {
|
|
||||||
for (x=0; x<4; x++) {
|
|
||||||
|
|
||||||
t = target[to];
|
|
||||||
s = pixels[so++];
|
|
||||||
|
|
||||||
i = ((sy + y) * 320 + (sx + x * 2));
|
|
||||||
pos = 7 - (i % 8);
|
|
||||||
i /= 8;
|
|
||||||
|
|
||||||
//***FIX*** Another endian order issue. Left & Right are swapped.
|
|
||||||
if ((stencil->pixels[i] & (1 << pos)) != 0) {
|
|
||||||
t.r = s.r;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = ((sy + y) * 320 + (sx + x * 2 + 1));
|
|
||||||
pos = 7 - (i % 8);
|
|
||||||
i /= 8;
|
|
||||||
|
|
||||||
if ((stencil->pixels[i] & (1 << pos)) != 0) {
|
|
||||||
t.l = s.l;
|
|
||||||
}
|
|
||||||
|
|
||||||
target[to++] = t;
|
|
||||||
}
|
|
||||||
so += 156;
|
|
||||||
to += 156;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//***FIX*** This no longer does what we want since blitting source addresses are now in pixels. Fix!
|
|
||||||
void jlDrawBlitMap(jint16 startX, jint16 startY, jint16 width, jint16 height, byte *mapData, juint16 stride, jlSurfaceT source) {
|
|
||||||
// startX = start tile for drawing - in pixels
|
|
||||||
// startY = start tile for drawing - in pixels
|
|
||||||
// width = tiles to draw horizontally - in tiles
|
|
||||||
// height = tiles to draw vertically - in tiles
|
|
||||||
// mapData = pointer to tile x/y pairs to draw
|
|
||||||
// stride = number of tile bytes to skip in the mapData for every horizontal line
|
|
||||||
// tiles = surface to fetch tile data from
|
|
||||||
jint16 x;
|
|
||||||
jint16 y;
|
|
||||||
byte tileX;
|
|
||||||
byte tileY;
|
|
||||||
juint16 offset = 0;
|
|
||||||
jint16 endX = width * 8 + startX;
|
|
||||||
jint16 endY = height * 8 + startY;
|
|
||||||
|
|
||||||
for (y=startY; y<endY; y+=8) {
|
|
||||||
for (x=startX; x<endX; x+=8) {
|
|
||||||
tileX = mapData[offset++];
|
|
||||||
tileY = mapData[offset++];
|
|
||||||
jlDrawBlit8x8(source, tileX, tileY, x, y);
|
|
||||||
}
|
|
||||||
offset += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawClear(void) {
|
|
||||||
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
|
||||||
memset(target, _jlDrawColorNibbles, 32000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawLine(jint16 x1, jint16 y1, jint16 x2, jint16 y2) {
|
|
||||||
jint16 x;
|
|
||||||
jint16 y;
|
|
||||||
jint16 dx;
|
|
||||||
jint16 dy;
|
|
||||||
jint16 incX;
|
|
||||||
jint16 incY;
|
|
||||||
jint16 balance;
|
|
||||||
|
|
||||||
if (x2 >= x1) {
|
|
||||||
dx = x2 - x1;
|
|
||||||
incX = 1;
|
|
||||||
} else {
|
|
||||||
dx = x1 - x2;
|
|
||||||
incX = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y2 >= y1) {
|
|
||||||
dy = y2 - y1;
|
|
||||||
incY = 1;
|
|
||||||
} else {
|
|
||||||
dy = y1 - y2;
|
|
||||||
incY = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = x1;
|
|
||||||
y = y1;
|
|
||||||
|
|
||||||
if (dx >= dy) {
|
|
||||||
dy <<= 1;
|
|
||||||
balance = dy - dx;
|
|
||||||
dx <<= 1;
|
|
||||||
while (x != x2) {
|
|
||||||
jlDrawPixelSet(x, y);
|
|
||||||
if (balance >= 0) {
|
|
||||||
y += incY;
|
|
||||||
balance -= dx;
|
|
||||||
}
|
|
||||||
balance += dy;
|
|
||||||
x += incX;
|
|
||||||
}
|
|
||||||
jlDrawPixelSet(x, y);
|
|
||||||
} else {
|
|
||||||
dx <<= 1;
|
|
||||||
balance = dx - dy;
|
|
||||||
dy <<= 1;
|
|
||||||
while (y != y2) {
|
|
||||||
jlDrawPixelSet(x, y);
|
|
||||||
if (balance >= 0) {
|
|
||||||
x += incX;
|
|
||||||
balance -= dy;
|
|
||||||
}
|
|
||||||
balance += dx;
|
|
||||||
y += incY;
|
|
||||||
}
|
|
||||||
jlDrawPixelSet(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
byte jlDrawPixelGet(jint16 x, jint16 y) {
|
|
||||||
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
|
||||||
int p = x / 2 + y * 160;
|
|
||||||
return (jlUtilIsOdd(x) ? target[p].l : target[p].r);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawPixelSet(jint16 x, jint16 y) {
|
|
||||||
jlPixelPairT *target = (jlPixelPairT *)_jlDrawTargetActual;
|
|
||||||
jlPixelPairT *pixelPair = target + (y * 160) + (x / 2);
|
|
||||||
if (jlUtilIsOdd(x)) {
|
|
||||||
pixelPair->l = _jlDrawColor;
|
|
||||||
} else {
|
|
||||||
pixelPair->r = _jlDrawColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jlSurfaceT jlDrawSurfaceGet(void) {
|
|
||||||
return _jlDrawTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlDrawSurfaceSet(jlSurfaceT target) {
|
|
||||||
_jlDrawTarget = target;
|
|
||||||
_jlDrawTargetActual = (target == NULL) ? (jlSurfaceT)_jlBackingStore->pixels : target;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jint16 jlGameGetAxis(byte which) {
|
|
||||||
SDL_GameControllerAxis axis;
|
|
||||||
short int unscaled;
|
|
||||||
short int max = SHRT_MIN;
|
|
||||||
short int min = SHRT_MAX;
|
|
||||||
int x;
|
|
||||||
|
|
||||||
jlUtilIdle();
|
|
||||||
|
|
||||||
if (which == 0) {
|
|
||||||
axis = SDL_CONTROLLER_AXIS_LEFTX;
|
|
||||||
} else {
|
|
||||||
axis = SDL_CONTROLLER_AXIS_LEFTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (x=0; x<_jlControllerCount; x++) {
|
|
||||||
unscaled = SDL_GameControllerGetAxis(_jlControllers[x], axis);
|
|
||||||
if (unscaled > 0) {
|
|
||||||
if (unscaled > max) {
|
|
||||||
max = unscaled;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (unscaled < min) {
|
|
||||||
min = unscaled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (-min > max ? min : max) / 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlGameGetButton(byte which) {
|
|
||||||
SDL_GameControllerButton button;
|
|
||||||
int x;
|
|
||||||
bool pressed = false;
|
|
||||||
|
|
||||||
jlUtilIdle();
|
|
||||||
|
|
||||||
if (which == 0) {
|
|
||||||
button = SDL_CONTROLLER_BUTTON_A;
|
|
||||||
} else {
|
|
||||||
button = SDL_CONTROLLER_BUTTON_B;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (x=0; x<_jlControllerCount; x++) {
|
|
||||||
if (SDL_GameControllerGetButton(_jlControllers[x], button)) {
|
|
||||||
pressed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _jlImgCreate(jlImgT **img) {
|
|
||||||
jlImgT *t = NULL;
|
|
||||||
|
|
||||||
// Are we loading into a new image?
|
|
||||||
if (*img == NULL) {
|
|
||||||
t = (jlImgT *)jlMalloc(sizeof(jlImgT));
|
|
||||||
if (t == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*img = t;
|
|
||||||
}
|
|
||||||
t = (jlImgT *)*img;
|
|
||||||
t->id[0] = 'I';
|
|
||||||
t->id[1] = 'M';
|
|
||||||
t->id[2] = 'G';
|
|
||||||
t->version = 0;
|
|
||||||
// Backing store does not exist at startup
|
|
||||||
if (_jlBackingStore != NULL) {
|
|
||||||
memcpy(t->palette, _jlBackingStore->palette, sizeof(t->palette));
|
|
||||||
memcpy(t->pixels, _jlBackingStore->pixels, sizeof(t->pixels));
|
|
||||||
}
|
|
||||||
*img = t;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlImgDisplay(jlImgT *img) {
|
|
||||||
memcpy(_jlBackingStore->palette, img->palette, sizeof(img->palette));
|
|
||||||
memcpy(_jlBackingStore->pixels, img->pixels, sizeof(img->pixels));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlKeyPressed(void) {
|
|
||||||
jlUtilIdle();
|
|
||||||
return (_jlNumKeysDown > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char jlKeyRead(void) {
|
|
||||||
jlUtilIdle();
|
|
||||||
return _jlLastKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlPaletteSet(byte index, byte r, byte g, byte b) {
|
|
||||||
_jlBackingStore->palette[index].r = r;
|
|
||||||
_jlBackingStore->palette[index].g = g;
|
|
||||||
_jlBackingStore->palette[index].b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlPaletteSetFromImg(jlImgT *img) {
|
|
||||||
memcpy(_jlBackingStore->palette, img->palette, sizeof(img->palette));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundFree(jlSoundT *sound) {
|
|
||||||
if (sound != NULL) {
|
|
||||||
if (sound->data != NULL) {
|
|
||||||
Mix_FreeChunk((Mix_Chunk *)sound->data);
|
|
||||||
}
|
|
||||||
jlFree(sound);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundIsPlaying(jlSoundT *sound) {
|
|
||||||
return (Mix_Playing(sound->channel) > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool _jlSoundLoad(jlSoundT **sound, char *filename) {
|
|
||||||
bool result = false;
|
|
||||||
Mix_Chunk *sample;
|
|
||||||
sample = Mix_LoadWAV(jlUtilMakePathname(filename, "ogg"));
|
|
||||||
if (sample) {
|
|
||||||
if (*sound != NULL) {
|
|
||||||
jlSoundFree(*sound);
|
|
||||||
}
|
|
||||||
jlSoundT *t = (jlSoundT *)jlMalloc(sizeof(jlSoundT));
|
|
||||||
if (t != NULL) {
|
|
||||||
t->data = (void *)sample;
|
|
||||||
*sound = t;
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiContinue(void) {
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundMidiIsPlaying(void) {
|
|
||||||
//***TODO***
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiPause(void) {
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiPlay(char *name) {
|
|
||||||
(void)name;
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundMidiStop(void) {
|
|
||||||
//***TODO***
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModContinue(void) {
|
|
||||||
Mix_ResumeMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlSoundModIsPlaying(void) {
|
|
||||||
return (bool)Mix_PlayingMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModPause(void) {
|
|
||||||
Mix_PauseMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModPlay(char *name) {
|
|
||||||
if (_jlMusicHandle != NULL) {
|
|
||||||
jlSoundModStop();
|
|
||||||
}
|
|
||||||
_jlMusicHandle = Mix_LoadMUS(jlUtilMakePathname(name, "mod"));
|
|
||||||
if (_jlMusicHandle != NULL) {
|
|
||||||
Mix_PlayMusic(_jlMusicHandle, 1);
|
|
||||||
} else {
|
|
||||||
printf("Unable to load %s\n", jlUtilMakePathname(name, "mod"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundModStop(void) {
|
|
||||||
if (jlSoundModIsPlaying()) {
|
|
||||||
Mix_HaltMusic();
|
|
||||||
}
|
|
||||||
if (_jlMusicHandle != NULL) {
|
|
||||||
Mix_FreeMusic(_jlMusicHandle);
|
|
||||||
_jlMusicHandle = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlSoundPlay(jlSoundT *sound) {
|
|
||||||
sound->channel = (jint16)Mix_PlayChannel(-1, (Mix_Chunk *)sound->data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char _jlUtilIdleCheckKey(int sym) {
|
|
||||||
|
|
||||||
char key = 0;
|
|
||||||
|
|
||||||
// Do we even care about this key?
|
|
||||||
if ((sym < 8) || (sym > 127)) {
|
|
||||||
key = 0;
|
|
||||||
} else {
|
|
||||||
SDL_Keymod mod = SDL_GetModState();
|
|
||||||
key = (char)sym;
|
|
||||||
// Map LF to CR
|
|
||||||
if (key == 10) {
|
|
||||||
key = 13;
|
|
||||||
}
|
|
||||||
// Map DEL to BS
|
|
||||||
if (key == 127) {
|
|
||||||
key = 8;
|
|
||||||
}
|
|
||||||
// Convert to uppercase if needed
|
|
||||||
if ((mod == KMOD_SHIFT) && (key >= 'a') && (key <= 'z')) {
|
|
||||||
key -= 32;
|
|
||||||
}
|
|
||||||
// Is this outside the range we care about?
|
|
||||||
if ((key < ' ') || (key > '~')) {
|
|
||||||
if ((key != 8) && (key != 13) && (key != 27)) {
|
|
||||||
key = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilIdle(void) {
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
switch(event.type) {
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
// Track keydown/keyup and remember last key pressed.
|
|
||||||
switch (event.key.keysym.sym) {
|
|
||||||
case SDLK_F1:
|
|
||||||
//SDL_RenderSetScale(_jlRenderer, 1.0f, 1.0f);
|
|
||||||
SDL_SetWindowSize(_jlWindow, 320, 200);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDLK_F2:
|
|
||||||
//SDL_RenderSetScale(_jlRenderer, 2.0f, 2.0f);
|
|
||||||
SDL_SetWindowSize(_jlWindow, 640, 400);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDLK_F3:
|
|
||||||
//SDL_RenderSetScale(_jlRenderer, 3.0f, 3.0f);
|
|
||||||
SDL_SetWindowSize(_jlWindow, 860, 600);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
_jlLastKey = _jlUtilIdleCheckKey(event.key.keysym.sym);
|
|
||||||
if (_jlLastKey > 0) {
|
|
||||||
//***FIX***
|
|
||||||
//_jlNumKeysDown++;
|
|
||||||
_jlNumKeysDown = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_KEYUP:
|
|
||||||
if (_jlUtilIdleCheckKey(event.key.keysym.sym) > 0) {
|
|
||||||
//***FIX***
|
|
||||||
//_jlNumKeysDown--;
|
|
||||||
_jlNumKeysDown = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_QUIT:
|
|
||||||
_jlIsRunning = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//printf("Keys down: %d\n", _jlNumKeysDown);
|
|
||||||
|
|
||||||
SDL_Delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool jlUtilMustExit(void) {
|
|
||||||
return !_jlIsRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilNibbleSwap(byte *mem, jint16 count, byte old, byte new) {
|
|
||||||
int x;
|
|
||||||
jlPixelPairT *b;
|
|
||||||
for (x=0; x<count; x++) {
|
|
||||||
b = (jlPixelPairT *)&mem[x];
|
|
||||||
if (b->l == old) b->l = new;
|
|
||||||
if (b->r == old) b->r = new;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilShutdown(void) {
|
|
||||||
int x;
|
|
||||||
|
|
||||||
SDL_RemoveTimer(_jlTimerId);
|
|
||||||
for (x=0; x<_jlControllerCount; x++) {
|
|
||||||
SDL_GameControllerClose(_jlControllers[x]);
|
|
||||||
}
|
|
||||||
_jlControllerCount = 0;
|
|
||||||
jlFree(_jlControllers);
|
|
||||||
jlImgFree(_jlBackingStore);
|
|
||||||
//***TODO*** Sound effects stop?
|
|
||||||
jlSoundModStop();
|
|
||||||
jlSoundMidiStop();
|
|
||||||
Mix_CloseAudio();
|
|
||||||
Mix_Quit();
|
|
||||||
SDL_FreeFormat(_jlPixelFormat);
|
|
||||||
SDL_DestroyTexture(_jlTexture);
|
|
||||||
SDL_DestroyRenderer(_jlRenderer);
|
|
||||||
SDL_DestroyWindow(_jlWindow);
|
|
||||||
SDL_Quit();
|
|
||||||
jlUtilDie("Clean Exit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void jlUtilStartup(char *appTitle) {
|
|
||||||
|
|
||||||
int flags;
|
|
||||||
int result;
|
|
||||||
int joysticks;
|
|
||||||
int x;
|
|
||||||
|
|
||||||
// Start low-level tools
|
|
||||||
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
|
|
||||||
jlUtilDie(SDL_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start audio mixer & music system.
|
|
||||||
flags = MIX_INIT_MOD;
|
|
||||||
result = Mix_Init(flags);
|
|
||||||
if ((result & flags) != flags) {
|
|
||||||
jlUtilDie(Mix_GetError());
|
|
||||||
}
|
|
||||||
if (Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1) {
|
|
||||||
jlUtilDie(Mix_GetError());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up controllers
|
|
||||||
joysticks = SDL_NumJoysticks();
|
|
||||||
_jlControllers = (SDL_GameController **)jlMalloc(sizeof(SDL_GameController *) * (unsigned long)joysticks);
|
|
||||||
for (x=0; x<joysticks; x++) {
|
|
||||||
if (SDL_IsGameController(x)) {
|
|
||||||
_jlControllers[_jlControllerCount++] = SDL_GameControllerOpen(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a window and renderer using SDL
|
|
||||||
_jlWindow = SDL_CreateWindow(appTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 400, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
|
||||||
_jlRenderer = SDL_CreateRenderer(_jlWindow, -1, SDL_RENDERER_SOFTWARE);
|
|
||||||
_jlTexture = SDL_CreateTexture(_jlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 200);
|
|
||||||
_jlPixelFormat = SDL_AllocFormat(SDL_GetWindowPixelFormat(_jlWindow));
|
|
||||||
|
|
||||||
// Create backing store
|
|
||||||
jlImgCreate(_jlBackingStore);
|
|
||||||
jlPaletteDefault();
|
|
||||||
jlDrawSurfaceSet(JOEY_DISPLAY);
|
|
||||||
jlDrawColorSet(0);
|
|
||||||
jlDrawClear();
|
|
||||||
jlDrawColorSet(15);
|
|
||||||
jlDisplayPresent();
|
|
||||||
|
|
||||||
// Start 1/60th second timer
|
|
||||||
_jlTimerId = SDL_AddTimer(1000 / 60, _jlUtilTimer, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Uint32 _jlUtilTimer(Uint32 interval, void *param) {
|
|
||||||
(void)param;
|
|
||||||
_jlTimerValue++;
|
|
||||||
return(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
juint16 jlUtilTimer(void) {
|
|
||||||
return _jlTimerValue;
|
|
||||||
}
|
|
|
@ -26,25 +26,18 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
#define JOEY_MAIN
|
|
||||||
#include "joey.h"
|
#include "joey.h"
|
||||||
#ifdef JOEY_IIGS
|
#ifdef JOEY_IIGS
|
||||||
segment "testapp";
|
segment "testapp";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef DMALLOC
|
|
||||||
#define DMALLOC_FUNC_CHECK
|
|
||||||
#include "dmalloc.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void help(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
void help(jlSurfaceT source, jlStnT *stencil, jint16 sx, jint16 sy, jint16 tx, jint16 ty) {
|
||||||
|
|
||||||
int mo; // Mask offset
|
int mo; // Mask offset
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
byte b; // Mask bit index
|
jbyte b; // Mask bit index
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
@ -180,14 +173,13 @@ void grid(void) {
|
||||||
for (i=0; i<320; i+=8) {
|
for (i=0; i<320; i+=8) {
|
||||||
jlDrawLine(i, 0, i, 199);
|
jlDrawLine(i, 0, i, 199);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0; i<200; i+=8) {
|
for (i=0; i<200; i+=8) {
|
||||||
jlDrawLine(0, i, 319, i);
|
jlDrawLine(0, i, 319, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
|
||||||
jlDisplayPresent();
|
jlDisplayPresent();
|
||||||
}
|
jlKeyWaitForAny();
|
||||||
jlKeyRead();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,7 +214,7 @@ void lineTest(void) {
|
||||||
y = 17;
|
y = 17;
|
||||||
fontPrint(font, NULL, 1, y++, "Drawing %s ", what);
|
fontPrint(font, NULL, 1, y++, "Drawing %s ", what);
|
||||||
|
|
||||||
jlDrawColorSet((byte)color);
|
jlDrawColorSet((jbyte)color);
|
||||||
if (phase < 2) {
|
if (phase < 2) {
|
||||||
jlDrawLine(x2, y2, 319-x2, 199-y2);
|
jlDrawLine(x2, y2, 319-x2, 199-y2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -272,7 +264,7 @@ void lineTest(void) {
|
||||||
|
|
||||||
jlDisplayPresent();
|
jlDisplayPresent();
|
||||||
|
|
||||||
jlDrawColorSet((byte)0);
|
jlDrawColorSet((jbyte)0);
|
||||||
if (op < 2) {
|
if (op < 2) {
|
||||||
jlDrawLine(ox, oy, 319-ox, 199-oy);
|
jlDrawLine(ox, oy, 319-ox, 199-oy);
|
||||||
} else {
|
} else {
|
||||||
|
@ -295,23 +287,43 @@ void lineTest(void) {
|
||||||
void musicTest(void) {
|
void musicTest(void) {
|
||||||
jlImgT *kanga = NULL;
|
jlImgT *kanga = NULL;
|
||||||
jlImgT *font = NULL;
|
jlImgT *font = NULL;
|
||||||
|
jlModT *music = NULL;
|
||||||
|
jlSoundT *sound1 = NULL;
|
||||||
|
jlSoundT *sound2 = NULL;
|
||||||
|
jbyte volume = 0;
|
||||||
|
|
||||||
if (!jlImgLoad(kanga, "kanga")) jlUtilDie("Unable to load kanga.img!");
|
if (!jlImgLoad(kanga, "kanga")) jlUtilDie("Unable to load kanga.img!");
|
||||||
if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!");
|
if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!");
|
||||||
|
if (!jlModLoad(music, "music")) jlUtilDie("Unable to load music!");
|
||||||
|
if (!jlSoundLoad(sound1, "applause")) jlUtilDie("Unable to load applause!");
|
||||||
|
if (!jlSoundLoad(sound2, "kongas")) jlUtilDie("Unable to load kongas!");
|
||||||
|
|
||||||
jlImgDisplay(kanga);
|
jlImgDisplay(kanga);
|
||||||
jlDisplayPresent();
|
jlDisplayPresent();
|
||||||
|
|
||||||
jlSoundModPlay("music");
|
jlModPlay(music);
|
||||||
|
|
||||||
|
//jlSoundSwapChannels(jtrue);
|
||||||
|
jlSoundPlay(sound1, CHANNEL_FRONT_LEFT, 255);
|
||||||
|
jlSoundPlay(sound2, CHANNEL_FRONT_RIGHT, 255);
|
||||||
|
|
||||||
|
jlDisplayBorder(BORDER_DEEP_BLUE);
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
while (!jlKeyPressed()) {
|
||||||
fontPrint(font, NULL, 1, 1, "%dx%d %d %d ", jlGameGetAxis(0), jlGameGetAxis(1), jlGameGetButton(0), jlGameGetButton(1));
|
fontPrint(font, NULL, 1, 1, "%dx%d %d %d ", jlGameGetAxis(0), jlGameGetAxis(1), jlGameGetButton(0), jlGameGetButton(1));
|
||||||
jlDisplayPresent();
|
jlDisplayPresent();
|
||||||
|
if (volume < 255) {
|
||||||
|
volume++;
|
||||||
|
jlModVolume(volume);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
jlKeyRead();
|
jlKeyRead();
|
||||||
|
|
||||||
jlSoundModStop();
|
jlModStop();
|
||||||
|
|
||||||
|
jlSoundFree(sound2);
|
||||||
|
jlSoundFree(sound1);
|
||||||
|
jlModFree(music);
|
||||||
jlImgFree(font);
|
jlImgFree(font);
|
||||||
jlImgFree(kanga);
|
jlImgFree(kanga);
|
||||||
}
|
}
|
||||||
|
@ -319,13 +331,14 @@ void musicTest(void) {
|
||||||
|
|
||||||
void showStencil(void) {
|
void showStencil(void) {
|
||||||
jlStnT *stencil = NULL;
|
jlStnT *stencil = NULL;
|
||||||
jint16 y;
|
juint16 y;
|
||||||
jint16 x;
|
juint16 x;
|
||||||
|
juint16 temp;
|
||||||
jint16 count;
|
jint16 count;
|
||||||
juint16 index;
|
jint16 index;
|
||||||
byte bit;
|
jbyte bit;
|
||||||
|
|
||||||
if (!jlStnLoad(stencil, "font")) jlUtilDie("Unable to load font.stn!");
|
if (!jlStnLoad(stencil, "biff")) jlUtilDie("Unable to load biff.stn!");
|
||||||
|
|
||||||
jlDrawColorSet(0);
|
jlDrawColorSet(0);
|
||||||
jlDrawClear();
|
jlDrawClear();
|
||||||
|
@ -354,12 +367,12 @@ void showStencil(void) {
|
||||||
jlDrawClear();
|
jlDrawClear();
|
||||||
jlDrawColorSet(15);
|
jlDrawColorSet(15);
|
||||||
|
|
||||||
// Draw stencil by pixel location - this fails on the IIgs
|
// Draw stencil by pixel location
|
||||||
for (y=0; y<200; y++) {
|
for (y=0; y<200; y++) {
|
||||||
for (x=0; x<320; x++) {
|
for (x=0; x<320; x++) {
|
||||||
index = (y * 320 + x);
|
temp = (y * 320 + x);
|
||||||
count = 7 - (index % 8);
|
count = 7 - (temp % 8);
|
||||||
index /= 8;
|
index = temp / 8;
|
||||||
bit = stencil->pixels[index];
|
bit = stencil->pixels[index];
|
||||||
if (bit & (1 << count)) {
|
if (bit & (1 << count)) {
|
||||||
jlDrawPixelSet(x, y);
|
jlDrawPixelSet(x, y);
|
||||||
|
@ -391,12 +404,9 @@ void stencilTest(void) {
|
||||||
|
|
||||||
while (!jlKeyPressed()) {
|
while (!jlKeyPressed()) {
|
||||||
// Draw Biff & grab background
|
// Draw Biff & grab background
|
||||||
for (x=0; x<319-32; x++) {
|
for (x=0; x<319-32; x+=2) {
|
||||||
for (i=0; i<3; i++) {
|
for (i=0; i<3; i++) {
|
||||||
for (j=0; j<4; j++) {
|
for (j=0; j<4; j++) {
|
||||||
jlDrawSurfaceSet(jlImgSurfaceGet(biffI));
|
|
||||||
jlDrawBlit8x8(JOEY_DISPLAY, x + (j * 8), y + (i * 8), j * 8 + 32, i * 8 + 32);
|
|
||||||
jlDrawSurfaceSet(JOEY_DISPLAY);
|
|
||||||
jlDrawBlit8x8a(jlImgSurfaceGet(biffI), biffS, j * 8, i * 8, x + (j * 8), y + (i * 8));
|
jlDrawBlit8x8a(jlImgSurfaceGet(biffI), biffS, j * 8, i * 8, x + (j * 8), y + (i * 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,7 +414,7 @@ void stencilTest(void) {
|
||||||
// Erase Biff
|
// Erase Biff
|
||||||
for (i=0; i<3; i++) {
|
for (i=0; i<3; i++) {
|
||||||
for (j=0; j<4; j++) {
|
for (j=0; j<4; j++) {
|
||||||
jlDrawBlit8x8(jlImgSurfaceGet(biffI), j * 8 + 32, i * 8 + 32, x + (j * 8), y + (i * 8));
|
jlDrawBlit8x8(jlImgSurfaceGet(kangaI), x + (j * 8), y + (i * 8), x + (j * 8), y + (i * 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for early quit
|
// Check for early quit
|
||||||
|
@ -439,17 +449,18 @@ void timerTest(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
void joeyMain(void) {
|
||||||
jlUtilStartup("JoeyLib Test");
|
|
||||||
|
jlUtilTitleSet("JoeyLib Test");
|
||||||
|
|
||||||
//blitTest();
|
//blitTest();
|
||||||
//exerciseAPI();
|
//exerciseAPI();
|
||||||
//grid();
|
//grid();
|
||||||
//lineTest();
|
//lineTest();
|
||||||
//musicTest();
|
musicTest();
|
||||||
//showStencil();
|
//showStencil();
|
||||||
stencilTest();
|
//stencilTest();
|
||||||
//timerTest();
|
//timerTest();
|
||||||
|
|
||||||
jlUtilShutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,34 @@
|
||||||
|
#
|
||||||
|
# JoeyLib
|
||||||
|
# Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
#
|
||||||
|
# This software is provided 'as-is', without any express or implied
|
||||||
|
# warranty. In no event will the authors be held liable for any damages
|
||||||
|
# arising from the use of this software.
|
||||||
|
#
|
||||||
|
# Permission is granted to anyone to use this software for any purpose,
|
||||||
|
# including commercial applications, and to alter it and redistribute it
|
||||||
|
# freely, subject to the following restrictions:
|
||||||
|
#
|
||||||
|
# 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
# claim that you wrote the original software. If you use this software
|
||||||
|
# in a product, an acknowledgment in the product documentation would be
|
||||||
|
# appreciated but is not required.
|
||||||
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
# misrepresented as being the original software.
|
||||||
|
# 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
#
|
||||||
|
|
||||||
# --- HERE BE DRAGONS ---
|
# --- HERE BE DRAGONS ---
|
||||||
|
|
||||||
function buildIIgs() {
|
function buildIIgs() {
|
||||||
TARGET=${JOEY}/sdks/IIgs/ORCA/out/${PROJECT}
|
TARGET=${JOEY}/sdks/IIgs/ORCA/out/${PROJECT}
|
||||||
GSTARGET=31:/out/${PROJECT}
|
GSTARGET=31:/out/${PROJECT}
|
||||||
CADIUS=${JOEY}/sdks/IIgs/cadius/cadius
|
CADIUS=${JOEY}/sdks/IIgs/cadius/cadius
|
||||||
VOL=Import
|
VOL=${PROJECT}
|
||||||
WORK=/tmp/IIgs
|
WORK=/tmp/IIgs
|
||||||
IMPORT=${WORK}/import.po
|
IMPORT=${WORK}/import.po
|
||||||
|
DEBUG=.
|
||||||
|
|
||||||
# Clean up target and working directories
|
# Clean up target and working directories
|
||||||
if [ -d ${TARGET} ]; then
|
if [ -d ${TARGET} ]; then
|
||||||
|
@ -19,6 +41,13 @@ function buildIIgs() {
|
||||||
fi
|
fi
|
||||||
mkdir -p ${WORK}
|
mkdir -p ${WORK}
|
||||||
|
|
||||||
|
# Where to put debugging information?
|
||||||
|
if [[ ! -z ${RESULTS} ]]; then
|
||||||
|
mkdir ${RESULTS}/IIgs
|
||||||
|
DEBUG=${RESULTS}/IIgs
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
#***TODO*** Add image and music conversion.
|
#***TODO*** Add image and music conversion.
|
||||||
# Automatically copy converted images, stencils, and audio so they
|
# Automatically copy converted images, stencils, and audio so they
|
||||||
# no longer need to be specified in the build script.
|
# no longer need to be specified in the build script.
|
||||||
|
@ -31,29 +60,48 @@ function buildIIgs() {
|
||||||
OFILES=""
|
OFILES=""
|
||||||
for F in "${CFILES[@]}"; do
|
for F in "${CFILES[@]}"; do
|
||||||
O=${F%.*}
|
O=${F%.*}
|
||||||
# If this file is named 'main' we don't add it to the link list until later
|
|
||||||
if [ "${O}" != "main" ]; then
|
|
||||||
OFILES="${OFILES} ${GSTARGET}/${O}"
|
OFILES="${OFILES} ${GSTARGET}/${O}"
|
||||||
fi
|
|
||||||
echo "Compiling ${F}..."
|
echo "Compiling ${F}..."
|
||||||
iix compile ${F} keep=${GSTARGET}/${O}
|
iix compile ${F} keep=${GSTARGET}/${O}
|
||||||
done
|
done
|
||||||
# Be sure 'main' is first in the link list
|
|
||||||
OFILES="${GSTARGET}/main ${OFILES} ${GSTARGET}/joeylib"
|
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
rm joey.h
|
rm joey.h
|
||||||
|
|
||||||
|
echo "Linking ${PROJECT}..."
|
||||||
|
|
||||||
# We need a local copy of joeylib to link
|
# We need a local copy of joeylib to link
|
||||||
cp -f ${JOEY}/dist/IIgs/joeylib#b20000 ${TARGET}/joeylib
|
cp -f ${JOEY}/dist/IIgs/jIIgsc.a#b10000 ${TARGET}/jIIgsc.a
|
||||||
iix chtyp -t lib ${GSTARGET}/joeylib
|
cp -f ${JOEY}/dist/IIgs/jIIgsc.root#b10000 ${TARGET}/jIIgsc.root
|
||||||
|
cp -f ${JOEY}/dist/IIgs/jIIgsasm.a#b10000 ${TARGET}/jIIgsasm.a
|
||||||
|
cp -f ${JOEY}/dist/IIgs/jIIgsasm.root#b10000 ${TARGET}/jIIgsasm.root
|
||||||
|
cp -f ${JOEY}/dist/IIgs/joey.a#b10000 ${TARGET}/joey.a
|
||||||
|
iix chtyp -t obj ${GSTARGET}/jIIgsc.a
|
||||||
|
iix chtyp -t obj ${GSTARGET}/jIIgsc.root
|
||||||
|
iix chtyp -t obj ${GSTARGET}/jIIgsasm.a
|
||||||
|
iix chtyp -t obj ${GSTARGET}/jIIgsasm.root
|
||||||
|
iix chtyp -t obj ${GSTARGET}/joey.a
|
||||||
|
|
||||||
|
# Add our library files to OFILES
|
||||||
|
OFILES="${GSTARGET}/jIIgsc ${GSTARGET}/joey ${GSTARGET}/jIIgsasm ${OFILES}"
|
||||||
|
|
||||||
# Link our program and create a map file
|
# Link our program and create a map file
|
||||||
iix -DKeepType=S16 link +L ${OFILES} keep=${GSTARGET}/${PROJECT}#b3db03 > ${PROJECT}.map
|
iix -DKeepType=S16 link +L ${OFILES} keep=${GSTARGET}/${PROJECT}#b3db03 > ${DEBUG}/${PROJECT}.map
|
||||||
|
|
||||||
# Create a disassembly and linker input listing
|
# Create a disassembly and linker input listing
|
||||||
iix dumpobj +D ${GSTARGET}/${PROJECT}#b3db03 &> ${PROJECT}.dis || true
|
iix dumpobj +D ${GSTARGET}/${PROJECT}#b3db03 &> ${DEBUG}/${PROJECT}.dis || true
|
||||||
echo ${OFILES} > ${PROJECT}.lnk
|
echo ${OFILES} > ${DEBUG}/${PROJECT}.lnk
|
||||||
|
|
||||||
|
# Clean up SYM files
|
||||||
|
CFILES=($(ls -1 *.c))
|
||||||
|
for F in "${CFILES[@]}"; do
|
||||||
|
O=${F%.*}.sym
|
||||||
|
if [[ -e ${O} ]]; then
|
||||||
|
rm ${O}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Creating disk image..."
|
||||||
|
|
||||||
# Be sure our work directories exists
|
# Be sure our work directories exists
|
||||||
mkdir -p ${WORK}/{data,source}
|
mkdir -p ${WORK}/{data,source}
|
||||||
|
@ -80,8 +128,12 @@ function buildIIgs() {
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ -z ${RESULTS} ]]; then
|
||||||
mkdir -p ${JOEY}/builds/${PROJECT}/IIgs/
|
mkdir -p ${JOEY}/builds/${PROJECT}/IIgs/
|
||||||
cp ${IMPORT} ${JOEY}/builds/${PROJECT}/IIgs/${PROJECT}.po
|
cp ${IMPORT} ${JOEY}/builds/${PROJECT}/IIgs/${PROJECT}.po
|
||||||
|
else
|
||||||
|
cp ${IMPORT} ${RESULTS}/IIgs/${PROJECT}.po
|
||||||
|
fi
|
||||||
|
|
||||||
# Did they ask for GSPlus to be executed?
|
# Did they ask for GSPlus to be executed?
|
||||||
if [ ! -z $1 ]; then
|
if [ ! -z $1 ]; then
|
||||||
|
|
|
@ -1,11 +1,40 @@
|
||||||
|
#
|
||||||
|
# JoeyLib
|
||||||
|
# Copyright (C) 2018-2019 Scott Duensing <scott@kangaroopunch.com>
|
||||||
|
#
|
||||||
|
# This software is provided 'as-is', without any express or implied
|
||||||
|
# warranty. In no event will the authors be held liable for any damages
|
||||||
|
# arising from the use of this software.
|
||||||
|
#
|
||||||
|
# Permission is granted to anyone to use this software for any purpose,
|
||||||
|
# including commercial applications, and to alter it and redistribute it
|
||||||
|
# freely, subject to the following restrictions:
|
||||||
|
#
|
||||||
|
# 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
# claim that you wrote the original software. If you use this software
|
||||||
|
# in a product, an acknowledgment in the product documentation would be
|
||||||
|
# appreciated but is not required.
|
||||||
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
# misrepresented as being the original software.
|
||||||
|
# 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
#
|
||||||
|
|
||||||
# --- HERE BE DRAGONS ---
|
# --- HERE BE DRAGONS ---
|
||||||
|
|
||||||
TARGET=${JOEY}/builds/${PROJECT}
|
|
||||||
WORK=/tmp/PC
|
|
||||||
|
|
||||||
|
|
||||||
function doPCBuild() {
|
function doPCBuild() {
|
||||||
|
|
||||||
|
local TARGET=
|
||||||
|
|
||||||
|
if [[ -z ${RESULTS} ]]; then
|
||||||
|
TARGET=${JOEY}/builds/${PROJECT}
|
||||||
|
else
|
||||||
|
TARGET=${RESULTS}
|
||||||
|
fi
|
||||||
|
|
||||||
|
local WORK=/tmp/PC
|
||||||
|
|
||||||
local OSNAME=$1
|
local OSNAME=$1
|
||||||
local OSARCH=$2
|
local OSARCH=$2
|
||||||
local EXT=$3
|
local EXT=$3
|
||||||
|
@ -37,8 +66,9 @@ function doPCBuild() {
|
||||||
${CC} ${CFLAGS} ${G_CFLAGS} -c ${F} -o ${WORK}/${O}.o
|
${CC} ${CFLAGS} ${G_CFLAGS} -c ${F} -o ${WORK}/${O}.o
|
||||||
done
|
done
|
||||||
|
|
||||||
# Link source & JoeyLib
|
# Link source & JoeyLib (JoeyLib listed twice on purpose.)
|
||||||
${CC} -o ${DEST}/${PROJECT}${EXT} ${OFILES} ${JOEY}/dist/${OSNAME}/${OSARCH}/libjoeylib.a ${LDFLAGS}
|
${CC} -o ${DEST}/${PROJECT}${EXT} ${OFILES} \
|
||||||
|
${JOEY}/dist/${OSNAME}/${OSARCH}/libjoeylib.a ${LDFLAGS} ${JOEY}/dist/${OSNAME}/${OSARCH}/libjoeylib.a
|
||||||
|
|
||||||
# Copy game data
|
# Copy game data
|
||||||
mkdir -p ${DEST}/data
|
mkdir -p ${DEST}/data
|
||||||
|
@ -67,7 +97,7 @@ function buildLinux64() {
|
||||||
function buildWindows32() {
|
function buildWindows32() {
|
||||||
CC="i686-w64-mingw32-gcc"
|
CC="i686-w64-mingw32-gcc"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS="-lgdi32 -lwinmm -limm32 -lversion -lole32 -loleaut32 -lsetupapi -static -lstdc++"
|
LDFLAGS="-mwindows -static -Wl,--start-group -lmingw32 -lgdi32 -lwinmm -limm32 -lversion -lole32 -loleaut32 -lsetupapi -lstdc++ -Wl,--end-group"
|
||||||
doPCBuild windows i386 '.exe'
|
doPCBuild windows i386 '.exe'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,22 +105,45 @@ function buildWindows32() {
|
||||||
function buildWindows64() {
|
function buildWindows64() {
|
||||||
CC="x86_64-w64-mingw32-gcc"
|
CC="x86_64-w64-mingw32-gcc"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS="-lgdi32 -lwinmm -limm32 -lversion -lole32 -loleaut32 -lsetupapi -static -lstdc++"
|
LDFLAGS="-mwindows -static -Wl,--start-group -lmingw32 -lgdi32 -lwinmm -limm32 -lversion -lole32 -loleaut32 -lsetupapi -lstdc++ -Wl,--end-group"
|
||||||
doPCBuild windows x64 '.exe'
|
doPCBuild windows x64 '.exe'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildmacOS32() {
|
function buildmacOSA64() {
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_APPLE_DEPLOYMENT_TARGET}"
|
||||||
|
export MACOSX_DARWIN="${MACOSX_APPLE_DARWIN}"
|
||||||
|
export PATH="${MACOSX_APPLE_PATH}:${JOEYPATH}"
|
||||||
|
export LD_LIBRARY_PATH="${MACOSX_APPLE_PATH}/../lib"
|
||||||
|
CC="oa64-clang"
|
||||||
|
CFLAGS=""
|
||||||
|
LDFLAGS="-lm -liconv -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-weak_framework,CoreHaptics -Wl,-weak_framework,GameController -Wl,-framework,ForceFeedback -lobjc -Wl,-framework,CoreVideo -Wl,-framework,Cocoa -Wl,-framework,Carbon -Wl,-framework,IOKit -Wl,-weak_framework,QuartzCore"
|
||||||
|
doPCBuild macOS a64 ''
|
||||||
|
export LD_LIBRARY_PATH=
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function buildmacOSX32() {
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_INTEL_DEPLOYMENT_TARGET}"
|
||||||
|
export MACOSX_DARWIN="${MACOSX_INTEL_DARWIN}"
|
||||||
|
export PATH="${MACOSX_INTEL_PATH}:${JOEYPATH}"
|
||||||
|
export LD_LIBRARY_PATH="${MACOSX_INTEL_PATH}/../lib"
|
||||||
CC="o32-clang"
|
CC="o32-clang"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS=""
|
LDFLAGS="-lm -liconv -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,ForceFeedback -lobjc -Wl,-framework,CoreVideo -Wl,-framework,Cocoa -Wl,-framework,Carbon -Wl,-framework,IOKit -Wl,-weak_framework,QuartzCore"
|
||||||
doPCBuild macos i386 ''
|
doPCBuild macOS i386 ''
|
||||||
|
export LD_LIBRARY_PATH=
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildmacOS64() {
|
function buildmacOSX64() {
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_INTEL_DEPLOYMENT_TARGET}"
|
||||||
|
export MACOSX_DARWIN="${MACOSX_INTEL_DARWIN}"
|
||||||
|
export PATH="${MACOSX_INTEL_PATH}:${JOEYPATH}"
|
||||||
|
export LD_LIBRARY_PATH="${MACOSX_INTEL_PATH}/../lib"
|
||||||
CC="o64-clang"
|
CC="o64-clang"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
LDFLAGS=""
|
LDFLAGS="-lm -liconv -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,ForceFeedback -lobjc -Wl,-framework,CoreVideo -Wl,-framework,Cocoa -Wl,-framework,Carbon -Wl,-framework,IOKit -Wl,-weak_framework,QuartzCore"
|
||||||
doPCBuild macos x64 ''
|
doPCBuild macOS x64 ''
|
||||||
|
export LD_LIBRARY_PATH=
|
||||||
}
|
}
|
||||||
|
|
1190
scripts/buildVm.sh
Executable file
1190
scripts/buildVm.sh
Executable file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue