368 lines
9.3 KiB
C
368 lines
9.3 KiB
C
/*
|
|
* Warehouse for JoeyLib - A Sokoban Clone
|
|
* 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.
|
|
*/
|
|
|
|
|
|
// Puzzles can be up to 20x12 in size.
|
|
// https://www.letslogic.com/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
|
|
#define MAX_WIDTH 20
|
|
#define MAX_HEIGHT 12
|
|
#define MAX_LINE 8192
|
|
|
|
|
|
typedef struct PuzzleS {
|
|
char puzzle[MAX_WIDTH][MAX_HEIGHT];
|
|
unsigned char crates;
|
|
unsigned char targets;
|
|
unsigned char width;
|
|
char height;
|
|
int16_t area;
|
|
} PuzzleT;
|
|
|
|
|
|
static char puzzleChars[] = { "_# .$@+*" };
|
|
|
|
|
|
int isLegal(char c) {
|
|
int x;
|
|
|
|
if (c == '-') c = ' ';
|
|
|
|
for (x=0; x<(int)strlen(puzzleChars); x++) {
|
|
if (puzzleChars[x] == c) {
|
|
return x + 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int dumbImporter(int argc, char *argv[]) {
|
|
|
|
char puzzle[MAX_WIDTH][MAX_WIDTH];
|
|
char line[MAX_LINE]; // Ooo! Look! Potential buffer overflow!
|
|
char height;
|
|
unsigned char width;
|
|
unsigned char b1;
|
|
unsigned char b2;
|
|
int16_t x;
|
|
int16_t y;
|
|
int16_t count;
|
|
int32_t bytes;
|
|
FILE *in;
|
|
FILE *puzzles;
|
|
FILE *index;
|
|
|
|
if (argc != 4) {
|
|
printf("%s: [letslogic.txt] [puzzles.dat] [index.dat]\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
in = fopen(argv[1], "rt");
|
|
if (!in) {
|
|
printf("Unable to read %s\n", argv[1]);
|
|
return 2;
|
|
}
|
|
|
|
puzzles = fopen(argv[2], "wb");
|
|
if (!puzzles) {
|
|
fclose(in);
|
|
printf("Unable to write %s\n", argv[2]);
|
|
return 2;
|
|
}
|
|
|
|
index = fopen(argv[3], "wb");
|
|
if (!index) {
|
|
fclose(puzzles);
|
|
fclose(in);
|
|
printf("Unable to write %s\n", argv[3]);
|
|
return 2;
|
|
}
|
|
|
|
// Skip two bytes at the front of the index file to later write the puzzle count into.
|
|
fputc(0, index);
|
|
fputc(0, index);
|
|
|
|
bytes = 0;
|
|
count = 0;
|
|
height = -1;
|
|
width = 0;
|
|
|
|
while (fgets(line, MAX_LINE, in)) {
|
|
// Is there a newline to remove?
|
|
if ((strlen(line) > 0) && (line[strlen(line) - 1] == '\n')) {
|
|
line[strlen(line) - 1] = 0;
|
|
}
|
|
// Is there anything on this line?
|
|
if (strlen(line) > 0) {
|
|
// Is this a puzzle line?
|
|
if (isLegal(line[0]) > 0) {
|
|
// Got it. Read this line.
|
|
if (width < strlen(line)) {
|
|
width = strlen(line);
|
|
//printf("Width %d\n", width);
|
|
}
|
|
height++;
|
|
for (x=0; x<width; x++) {
|
|
puzzle[x][(int)height] = line[x];
|
|
}
|
|
} else {
|
|
// Not a puzzle line. Were we currently reading a puzzle?
|
|
if (height >= 0) {
|
|
// Yep! Output it!
|
|
height++; // Make it 1 based ('width' has been 1 based the whole time)
|
|
if ((width & 1) == 1) {
|
|
// Width must be even - fill new column with empty space
|
|
for (y=0; y<height; y++) {
|
|
puzzle[width][y] = '_';
|
|
}
|
|
width++;
|
|
//printf("Increased width to %d\n", width);
|
|
}
|
|
// Save to index
|
|
fwrite(&bytes, sizeof(int32_t), 1, index);
|
|
// Save puzzle
|
|
fputc(width, puzzles);
|
|
fputc(height, puzzles);
|
|
bytes += 2;
|
|
// Write out the puzzle data, two tiles to a byte.
|
|
//printf("%dx%d\n", width, height);
|
|
for (y=0; y<height; y++) {
|
|
for (x=0; x<width; x+=2) {
|
|
b1 = isLegal(puzzle[x][y]) - 1;
|
|
b2 = isLegal(puzzle[x + 1][y]) - 1;
|
|
//printf("%c%c", puzzleChars[b1], puzzleChars[b2]);
|
|
fputc((b1 << 4) + b2, puzzles);
|
|
bytes++;
|
|
}
|
|
//printf("\n");
|
|
}
|
|
// Reset for next pass.
|
|
count++;
|
|
width = 0;
|
|
height = -1;
|
|
printf("Puzzle %d imported, %d total bytes written.\n", count, bytes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Seek back to the top and write the number of puzzles found.
|
|
fflush(index);
|
|
fseek(index, 0, SEEK_SET);
|
|
fwrite(&count, sizeof(int16_t), 1, index);
|
|
|
|
fclose(index);
|
|
fclose(puzzles);
|
|
fclose(in);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int sortByCrates(const void *a, const void *b) {
|
|
PuzzleT *p1 = (PuzzleT *)a;
|
|
PuzzleT *p2 = (PuzzleT *)b;
|
|
|
|
return (p1->crates - p2->crates);
|
|
}
|
|
|
|
|
|
int sortBySize(const void *a, const void *b) {
|
|
PuzzleT *p1 = (PuzzleT *)a;
|
|
PuzzleT *p2 = (PuzzleT *)b;
|
|
|
|
return (p1->area - p2->area);
|
|
}
|
|
|
|
|
|
int sortingImporter(int argc, char *argv[]) {
|
|
|
|
char *letsLogicIn;
|
|
char *puzzleOut;
|
|
char *indexOut;
|
|
char line[MAX_LINE]; // Ooo! Look! Potential buffer overflow!
|
|
unsigned char b1;
|
|
unsigned char b2;
|
|
int16_t count;
|
|
int16_t size;
|
|
int16_t x;
|
|
int16_t y;
|
|
int16_t z;
|
|
int32_t bytes;
|
|
FILE *in;
|
|
FILE *puzzlesFile;
|
|
FILE *indexFile;
|
|
PuzzleT *puzzles;
|
|
PuzzleT *temp;
|
|
|
|
if (argc != 4) {
|
|
printf("%s: [letslogic.txt] [puzzles.dat] [index.dat]\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
letsLogicIn = argv[1];
|
|
puzzleOut = argv[2];
|
|
indexOut = argv[3];
|
|
|
|
// Open exported puzzles file from LetsLogic.
|
|
in = fopen(letsLogicIn, "rt");
|
|
if (!in) {
|
|
printf("Unable to read %s\n", letsLogicIn);
|
|
return 2;
|
|
}
|
|
|
|
// Start with enough RAM for 100 puzzles.
|
|
size = 100;
|
|
puzzles = (PuzzleT *)malloc(sizeof(PuzzleT) * size);
|
|
|
|
count = 0;
|
|
puzzles[count].crates = 0;
|
|
puzzles[count].targets = 0;
|
|
puzzles[count].area = 0;
|
|
puzzles[count].width = 0;
|
|
puzzles[count].height = -1;
|
|
|
|
// Read all puzzles into memory.
|
|
while (fgets(line, MAX_LINE, in)) {
|
|
// Is there a newline to remove?
|
|
if ((strlen(line) > 0) && (line[strlen(line) - 1] == '\n')) {
|
|
line[strlen(line) - 1] = 0;
|
|
}
|
|
// Is there anything on this line?
|
|
if (strlen(line) > 0) {
|
|
// Is this a puzzle line?
|
|
if (isLegal(line[0]) > 0) {
|
|
// Got it. Read this line.
|
|
if (puzzles[count].width < strlen(line)) {
|
|
puzzles[count].width = strlen(line);
|
|
}
|
|
puzzles[count].height++;
|
|
for (x=0; x<puzzles[count].width; x++) {
|
|
if ((line[x] == '$') || (line[x] == '*')) {
|
|
puzzles[count].crates++;
|
|
}
|
|
if ((line[x] == '.') || (line[x] == '+') || (line[x] == '*')) {
|
|
puzzles[count].targets++;
|
|
}
|
|
puzzles[count].puzzle[x][(int)puzzles[count].height] = line[x];
|
|
}
|
|
} else {
|
|
// Not a puzzle line. Were we currently reading a puzzle?
|
|
if (puzzles[count].height >= 0) {
|
|
// Yep! Finalize it!
|
|
puzzles[count].height++; // Make it 1 based ('width' has been 1 based the whole time)
|
|
if ((puzzles[count].width & 1) == 1) {
|
|
// Width must be even - fill new column with empty space
|
|
for (y=0; y<puzzles[count].height; y++) {
|
|
puzzles[count].puzzle[puzzles[count].width][y] = '_';
|
|
}
|
|
puzzles[count].width++;
|
|
}
|
|
// Is this a valid size for a puzzle?
|
|
if ((puzzles[count].width > 2) && (puzzles[count].height > 2)) {
|
|
puzzles[count].area = puzzles[count].width * puzzles[count].height;
|
|
//printf("Puzzle %d is %dx%d with %d crates\n", count, puzzles[count].width, puzzles[count].height, puzzles[count].crates);
|
|
count++;
|
|
// Do we need more RAM?
|
|
if (count >= size) {
|
|
size += 100;
|
|
temp = (PuzzleT *)realloc(puzzles, sizeof(PuzzleT) * size);
|
|
if (!temp) {
|
|
printf("Unable to allocate RAM for puzzles!\n");
|
|
return 3;
|
|
}
|
|
puzzles = temp;
|
|
}
|
|
}
|
|
// Reset for next pass.
|
|
puzzles[count].crates = 0;
|
|
puzzles[count].targets = 0;
|
|
puzzles[count].area = 0;
|
|
puzzles[count].width = 0;
|
|
puzzles[count].height = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(in);
|
|
|
|
// Sort it for some kind of difficulty ramp.
|
|
qsort(puzzles, count, sizeof(PuzzleT), sortBySize);
|
|
qsort(puzzles, count, sizeof(PuzzleT), sortByCrates);
|
|
|
|
// Write puzzles in our format.
|
|
puzzlesFile = fopen(puzzleOut, "wb");
|
|
if (!puzzles) {
|
|
printf("Unable to write %s\n", puzzleOut);
|
|
return 4;
|
|
}
|
|
|
|
indexFile = fopen(indexOut, "wb");
|
|
if (!indexOut) {
|
|
fclose(puzzlesFile);
|
|
printf("Unable to write %s\n", indexOut);
|
|
return 5;
|
|
}
|
|
|
|
// Write number of puzzles into index.
|
|
fwrite(&count, sizeof(int16_t), 1, indexFile);
|
|
|
|
bytes = 0;
|
|
for (z=0; z<count; z++) {
|
|
// Save to index
|
|
fwrite(&bytes, sizeof(int32_t), 1, indexFile);
|
|
// Save puzzle
|
|
fputc(puzzles[z].width, puzzlesFile);
|
|
fputc(puzzles[z].height, puzzlesFile);
|
|
bytes += 2;
|
|
// Write out the puzzle data, two tiles to a byte.
|
|
for (y=0; y<puzzles[z].height; y++) {
|
|
for (x=0; x<puzzles[z].width; x+=2) {
|
|
b1 = isLegal(puzzles[z].puzzle[x][y]) - 1;
|
|
b2 = isLegal(puzzles[z].puzzle[x + 1][y]) - 1;
|
|
fputc((b1 << 4) + b2, puzzlesFile);
|
|
bytes++;
|
|
}
|
|
}
|
|
//if (puzzles[z].crates != puzzles[z].targets)
|
|
printf("Puzzle %d is %dx%d with %d crates %d targets\n", z, puzzles[z].width, puzzles[z].height, puzzles[z].crates, puzzles[z].targets);
|
|
}
|
|
|
|
fclose(indexFile);
|
|
fclose(puzzlesFile);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
//return dumbImporter(argc, argv);
|
|
return sortingImporter(argc, argv);
|
|
}
|