summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--maps/GoldenrodCity.asm12
-rw-r--r--maps/GoldenrodGameCorner.asm20
-rw-r--r--tools/Makefile1
-rw-r--r--tools/lzcomp.c139
5 files changed, 141 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index 14235ff3b..3dbd997de 100644
--- a/Makefile
+++ b/Makefile
@@ -96,7 +96,7 @@ pokecrystal11.gbc: $(crystal11_obj) pokecrystal.link
$(eval filename := $@.$(hash))
$(if $(wildcard $(filename)),\
cp $(filename) $@,\
- tools/lzcomp $< $@)
+ tools/lzcomp -- $< $@)
### Terrible hacks to match animations. Delete these rules if you don't care about matching.
diff --git a/maps/GoldenrodCity.asm b/maps/GoldenrodCity.asm
index 51041345f..6afe0df5c 100644
--- a/maps/GoldenrodCity.asm
+++ b/maps/GoldenrodCity.asm
@@ -13,7 +13,7 @@
const GOLDENRODCITY_ROCKET4
const GOLDENRODCITY_ROCKET5
const GOLDENRODCITY_ROCKET6
- const GOLDENRODCITY_POKEFAN_M2
+ const GOLDENRODCITY_MOVETUTOR
GoldenrodCity_MapScripts:
db 0 ; scene scripts
@@ -40,13 +40,13 @@ GoldenrodCity_MapScripts:
ifequal WEDNESDAY, .MoveTutorAppear
ifequal SATURDAY, .MoveTutorAppear
.MoveTutorDisappear:
- disappear GOLDENRODCITY_POKEFAN_M2
+ disappear GOLDENRODCITY_MOVETUTOR
return
.MoveTutorAppear:
checkflag ENGINE_DAILY_MOVE_TUTOR
iftrue .MoveTutorDone
- appear GOLDENRODCITY_POKEFAN_M2
+ appear GOLDENRODCITY_MOVETUTOR
.MoveTutorDone:
return
@@ -130,14 +130,14 @@ MoveTutorScript:
closetext
checkcode VAR_FACING
ifequal LEFT, .WalkAroundPlayer
- applymovement GOLDENRODCITY_POKEFAN_M2, MovementData_0x198a5f
+ applymovement GOLDENRODCITY_MOVETUTOR, MovementData_0x198a5f
jump .GoInside
.WalkAroundPlayer:
- applymovement GOLDENRODCITY_POKEFAN_M2, MovementData_0x198a63
+ applymovement GOLDENRODCITY_MOVETUTOR, MovementData_0x198a63
.GoInside:
playsound SFX_ENTER_DOOR
- disappear GOLDENRODCITY_POKEFAN_M2
+ disappear GOLDENRODCITY_MOVETUTOR
clearevent EVENT_GOLDENROD_GAME_CORNER_MOVE_TUTOR
setflag ENGINE_DAILY_MOVE_TUTOR
waitsfx
diff --git a/maps/GoldenrodGameCorner.asm b/maps/GoldenrodGameCorner.asm
index 288ec01d3..fbffa8229 100644
--- a/maps/GoldenrodGameCorner.asm
+++ b/maps/GoldenrodGameCorner.asm
@@ -10,15 +10,15 @@
const GOLDENRODGAMECORNER_COOLTRAINER_F
const GOLDENRODGAMECORNER_GENTLEMAN
const GOLDENRODGAMECORNER_POKEFAN_M2
- const GOLDENRODGAMECORNER_POKEFAN_M3
+ const GOLDENRODGAMECORNER_MOVETUTOR
GoldenrodGameCorner_MapScripts:
db 0 ; scene scripts
db 1 ; callbacks
- callback MAPCALLBACK_OBJECTS, .Callback
+ callback MAPCALLBACK_OBJECTS, .MoveTutor
-.Callback:
+.MoveTutor:
checkevent EVENT_BEAT_ELITE_FOUR
iffalse .finish
checkitem COIN_CASE
@@ -27,23 +27,23 @@ GoldenrodGameCorner_MapScripts:
ifequal WEDNESDAY, .move_tutor_outside
ifequal SATURDAY, .move_tutor_outside
.move_tutor_inside
- appear GOLDENRODGAMECORNER_POKEFAN_M3
+ appear GOLDENRODGAMECORNER_MOVETUTOR
return
.move_tutor_outside
checkflag ENGINE_DAILY_MOVE_TUTOR
iftrue .finish
- disappear GOLDENRODGAMECORNER_POKEFAN_M3
+ disappear GOLDENRODGAMECORNER_MOVETUTOR
.finish
return
-GoldenrodGameCornerPokefanM3Script:
+MoveTutorInsideScript:
faceplayer
opentext
- writetext GoldenrodGameCornerPokefanM3Text
+ writetext MoveTutorInsideText
waitbutton
closetext
- turnobject GOLDENRODGAMECORNER_POKEFAN_M3, RIGHT
+ turnobject GOLDENRODGAMECORNER_MOVETUTOR, RIGHT
end
GoldenrodGameCornerCoinVendorScript:
@@ -422,7 +422,7 @@ GoldenrodGameCornerPokefanM2Text:
line "UNDERGROUND."
done
-GoldenrodGameCornerPokefanM3Text:
+MoveTutorInsideText:
text "Wahahah! The coins"
line "keep rolling in!"
done
@@ -488,4 +488,4 @@ GoldenrodGameCorner_MapEvents:
object_event 10, 3, SPRITE_COOLTRAINER_F, SPRITEMOVEDATA_WANDER, 2, 1, -1, -1, PAL_NPC_GREEN, OBJECTTYPE_SCRIPT, 0, GoldenrodGameCornerCooltrainerFScript, -1
object_event 5, 10, SPRITE_GENTLEMAN, SPRITEMOVEDATA_STANDING_RIGHT, 0, 0, -1, -1, PAL_NPC_BLUE, OBJECTTYPE_SCRIPT, 0, GoldenrodGameCornerGentlemanScript, -1
object_event 2, 9, SPRITE_POKEFAN_M, SPRITEMOVEDATA_WANDER, 1, 1, -1, -1, PAL_NPC_BROWN, OBJECTTYPE_SCRIPT, 0, GoldenrodGameCornerPokefanM2Script, -1
- object_event 17, 10, SPRITE_POKEFAN_M, SPRITEMOVEDATA_STANDING_RIGHT, 0, 0, -1, -1, PAL_NPC_RED, OBJECTTYPE_SCRIPT, 0, GoldenrodGameCornerPokefanM3Script, EVENT_GOLDENROD_GAME_CORNER_MOVE_TUTOR
+ object_event 17, 10, SPRITE_POKEFAN_M, SPRITEMOVEDATA_STANDING_RIGHT, 0, 0, -1, -1, PAL_NPC_RED, OBJECTTYPE_SCRIPT, 0, MoveTutorInsideScript, EVENT_GOLDENROD_GAME_CORNER_MOVE_TUTOR
diff --git a/tools/Makefile b/tools/Makefile
index 4a5d51507..d29d5d748 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -19,5 +19,6 @@ clean:
rm -f $(tools)
gfx md5: common.h
+
%: %.c
$(CC) $(CFLAGS) -o $@ $<
diff --git a/tools/lzcomp.c b/tools/lzcomp.c
index 7d3e0b8fc..f2669aef8 100644
--- a/tools/lzcomp.c
+++ b/tools/lzcomp.c
@@ -11,13 +11,24 @@ struct command {
signed value: 17;
};
+struct options {
+ const char * input;
+ const char * output;
+ unsigned char mode;
+};
+
int main(int, char **);
+struct options get_options(char **);
+void usage(const char *);
void error_exit(int, const char *, ...);
void bit_flip(const unsigned char *, unsigned short, unsigned char *);
unsigned char * read_file_into_buffer(const char *, unsigned short *);
+void write_commands_to_textfile(const char *, const struct command *, unsigned, const unsigned char *);
+void write_command_to_textfile(FILE *, struct command, const unsigned char *);
void write_commands_to_file(const char *, const struct command *, unsigned, const unsigned char *);
void write_command_to_file(FILE *, struct command, const unsigned char *);
struct command * compress(const unsigned char *, unsigned short *);
+struct command * store_uncompressed(unsigned short *);
struct command * try_compress(const unsigned char *, const unsigned char *, unsigned short *, unsigned);
struct command find_best_copy(const unsigned char *, unsigned short, unsigned short, const unsigned char *, unsigned);
unsigned short scan_forwards(const unsigned char *, unsigned short, const unsigned char *, unsigned short, short *);
@@ -32,20 +43,50 @@ struct command * select_command_sequence(struct command **, const unsigned short
struct command * merge_command_sequences(const struct command *, unsigned short, const struct command *, unsigned short, unsigned short *);
unsigned short compressed_length(const struct command *, unsigned short);
-int main (int argc, char ** argv) {
- if (argc < 3) {
- fprintf(stderr, "usage: %s <source file> <compressed output>\n", *argv);
- return 3;
- }
+int main (int argc __attribute__((unused)), char ** argv) {
+ struct options options = get_options(argv);
unsigned short size;
- unsigned char * file_buffer = read_file_into_buffer(argv[1], &size);
+ unsigned char * file_buffer = read_file_into_buffer(options.input, &size);
struct command * compressed = compress(file_buffer, &size);
- write_commands_to_file(argv[2], compressed, size, file_buffer);
+ if (options.mode)
+ write_commands_to_textfile(options.output, compressed, size, file_buffer);
+ else
+ write_commands_to_file(options.output, compressed, size, file_buffer);
free(file_buffer);
free(compressed);
return 0;
}
+struct options get_options (char ** argv) {
+ struct options result = {.input = NULL, .output = NULL, .mode = 0};
+ const char * program_name = *argv;
+ for (argv ++; *argv; argv ++) {
+ if (strncmp(*argv, "--", 2)) break;
+ if (!strcmp(*argv, "--")) {
+ argv ++;
+ break;
+ } else if (!strcmp(*argv, "--text"))
+ result.mode = 1;
+ else if (!strcmp(*argv, "--binary"))
+ result.mode = 0;
+ else
+ error_exit(3, "unknown option: %s", *argv);
+ }
+ if (!*argv) usage(program_name);
+ result.input = *argv;
+ result.output = argv[1];
+ return result;
+}
+
+void usage (const char * program_name) {
+ fprintf(stderr, "Usage: %s [<options>] <source file> [<compressed output>]\n\n", program_name);
+ fputs("Options:\n", stderr);
+ fputs(" --text Output the command stream as text.\n", stderr);
+ fputs(" --binary Output the command stream as binary data (default).\n", stderr);
+ fputs(" -- End of option list.\n", stderr);
+ exit(3);
+}
+
void error_exit (int error_code, const char * error, ...) {
va_list ap;
va_start(ap, error);
@@ -77,13 +118,63 @@ unsigned char * read_file_into_buffer (const char * file, unsigned short * size)
return buf;
}
+void write_commands_to_textfile (const char * file, const struct command * commands, unsigned count, const unsigned char * input_stream) {
+ FILE * fp = file ? fopen(file, "w") : stdout;
+ if (!fp) error_exit(1, "could not open file %s for writing", file);
+ while (count --) write_command_to_textfile(fp, *(commands ++), input_stream);
+ if (fputs("\tlzend\n", fp) < 0) error_exit(1, "could not write terminator to compressed output");
+ if (file) fclose(fp);
+}
+
+void write_command_to_textfile (FILE * fp, struct command command, const unsigned char * input_stream) {
+ if ((!command.count) || (command.count > 1024)) error_exit(2, "invalid command in output stream");
+ int rv, pos;
+ const char * kind;
+ switch (command.command) {
+ case 0:
+ if ((rv = fprintf(fp, "\tlzdata")) < 0) break;
+ for (pos = 0; pos < command.count; pos ++) if ((rv = fprintf(fp, "%s$%02hhx", pos ? ", " : " ", input_stream[command.value + pos])) < 0) break;
+ rv = putc('\n', fp);
+ break;
+ case 1:
+ if ((command.value < 0) || (command.value > 255)) error_exit(2, "invalid command in output stream");
+ rv = fprintf(fp, "\tlzrepeat %u, $%02hhx\n", command.count, (unsigned char) command.value);
+ break;
+ case 2:
+ if (command.value < 0) error_exit(2, "invalid command in output stream");
+ rv = fprintf(fp, "\tlzrepeat %u, $%02hhx, $%02hhx\n", command.count, (unsigned char) command.value, (unsigned char) (command.value >> 8));
+ break;
+ case 3:
+ rv = fprintf(fp, "\tlzzero %u\n", command.count);
+ break;
+ case 4:
+ kind = "normal";
+ goto copy;
+ case 5:
+ kind = "flipped";
+ goto copy;
+ case 6:
+ kind = "reversed";
+ copy:
+ if ((command.value < -128) || (command.value > 32767)) error_exit(2, "invalid command in output stream");
+ if (command.value < 0)
+ rv = fprintf(fp, "\tlzcopy %s, %u, %d\n", kind, command.count, command.value);
+ else
+ rv = fprintf(fp, "\tlzcopy %s, %u, $%04hx\n", kind, command.count, (unsigned short) command.value);
+ break;
+ default:
+ error_exit(2, "invalid command in output stream");
+ }
+ if (rv < 0) error_exit(1, "could not write command to compressed output");
+}
+
void write_commands_to_file (const char * file, const struct command * commands, unsigned count, const unsigned char * input_stream) {
- FILE * fp = fopen(file, "wb");
+ FILE * fp = file ? fopen(file, "wb") : stdout;
if (!fp) error_exit(1, "could not open file %s for writing", file);
while (count --) write_command_to_file(fp, *(commands ++), input_stream);
unsigned char terminator = -1;
if (fwrite(&terminator, 1, 1, fp) != 1) error_exit(1, "could not write terminator to compressed output");
- fclose(fp);
+ if (file) fclose(fp);
}
void write_command_to_file (FILE * fp, struct command command, const unsigned char * input_stream) {
@@ -113,7 +204,7 @@ void write_command_to_file (FILE * fp, struct command command, const unsigned ch
*(pos ++) = command.value;
}
}
- if ((int)fwrite(buf, 1, pos - buf, fp) != (pos - buf)) error_exit(1, "could not write command to compressed output");
+ if (fwrite(buf, 1, pos - buf, fp) != (size_t)(pos - buf)) error_exit(1, "could not write command to compressed output");
if (command.command) return;
command.count ++;
if (fwrite(input_stream + command.value, 1, command.count, fp) != command.count) error_exit(1, "could not write data to compressed output");
@@ -122,16 +213,31 @@ void write_command_to_file (FILE * fp, struct command command, const unsigned ch
struct command * compress (const unsigned char * data, unsigned short * size) {
unsigned char * bitflipped = malloc(*size);
bit_flip(data, *size, bitflipped);
- struct command * compressed_sequences[COMPRESSION_METHODS];
- unsigned short lengths[COMPRESSION_METHODS];
+ struct command * compressed_sequences[COMPRESSION_METHODS + 1];
+ unsigned short lengths[COMPRESSION_METHODS + 1];
unsigned current;
for (current = 0; current < COMPRESSION_METHODS; current ++) {
lengths[current] = *size;
compressed_sequences[current] = try_compress(data, bitflipped, lengths + current, current);
}
free(bitflipped);
- struct command * result = select_command_sequence(compressed_sequences, lengths, COMPRESSION_METHODS, size);
- for (current = 0; current < COMPRESSION_METHODS; current ++) free(compressed_sequences[current]);
+ lengths[COMPRESSION_METHODS] = *size;
+ compressed_sequences[COMPRESSION_METHODS] = store_uncompressed(lengths + COMPRESSION_METHODS);
+ struct command * result = select_command_sequence(compressed_sequences, lengths, COMPRESSION_METHODS + 1, size);
+ for (current = 0; current <= COMPRESSION_METHODS; current ++) free(compressed_sequences[current]);
+ return result;
+}
+
+struct command * store_uncompressed (unsigned short * size) {
+ unsigned short position, block, remainder = *size;
+ struct command * result = NULL;
+ *size = 0;
+ for (position = 0; remainder; position += block, remainder -= block) {
+ block = (remainder > 1024) ? 1024 : remainder;
+ if ((block <= 64) && (block > 32)) block = 32;
+ result = realloc(result, sizeof(struct command) * (1 + *size));
+ result[(*size) ++] = (struct command) {.command = 0, .count = block, .value = position};
+ }
return result;
}
@@ -220,7 +326,8 @@ unsigned short scan_backwards (const unsigned char * data, unsigned short limit,
unsigned short position;
for (position = 0; position < real_position; position ++) {
if (data[position] != data[real_position]) continue;
- for (current_length = 0; (current_length < limit) && (data[position - current_length] == data[real_position + current_length]); current_length ++);
+ for (current_length = 0; (current_length <= position) && (current_length < limit) &&
+ (data[position - current_length] == data[real_position + current_length]); current_length ++);
if (current_length > 1024) current_length = 1024;
if (current_length < best_length) continue;
best_match = position;
@@ -310,7 +417,7 @@ void optimize (struct command * commands, unsigned short count) {
break;
case 1:
if (commands -> value != next -> value) break;
- // falls through
+ // fall through
case 3:
if ((commands -> count + next -> count) <= 1024) {
commands -> count += next -> count;