A very simplistic dumper for file entries for Clockwork Knight 1. I'm hoping to expand this so that I can reverse engineer the archive format used.

Format
C
Post date
2022-09-11 05:57
Période de publication
Illimité
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <ctype.h>
  4. static char buffer[8192];
  5. /* The first file in the archive has this name.
  6. * Because I don't know what header before the file entry table looks like
  7. * yet, I'm just going to skip past this first readable entry.
  8. */
  9. static const char key[] = "A.BIN;1";
  10. /* Tries to sychronize the end of a file entry. We know the following about
  11. * file entries:
  12. * File entries are 34 bytes, plus the name string, which may have nul-
  13. * padding.
  14. * File entries seem to start with 0x0030, 0x0028, or 0x002E.
  15. * 2E: All levels and menus, though "SNDDRV.BIN" also?
  16. * 30: Cinepak (CPK) movies and "SNDTBL" (soundfonts?), but also:
  17. * GAMEOVER.BIN (game over screen?)
  18. * PLAYERCG.BIN (continue screen?)
  19. * PLAYERTB.BIN (???)
  20. * 28: CDDA, maybe CD audio descriptors for the other 2 CD tracks?
  21. * Movies in the CPK format (Cinepak, documented elsewhere) start with 30.
  22. * Names are zero-padded, but this seems to pad them to an odd byte offset.
  23. * This might indicate the leading byte is actually a trailing byte?
  24. * Names have a leading size byte, which includes the length of a trailing
  25. * ";1". This is probably not actually part of the name, since there is
  26. * another file name table in the ROM that doesn't include this suffix.
  27. * Names consist of uppercase ASCII, decimal digits, and full-stops. I think
  28. * they are just DOS or UDF names.
  29. *
  30. * This function tries to find the end of file entry by finding the length
  31. * byte for the name, which is then validated by seeing if it points just past
  32. * a ";1", and all the characters in between are uppercase, punctuation, or
  33. * digits.
  34. *
  35. * This could probably be foiled, but it seems to work for the Clockwork Knight
  36. * ROM at least
  37. */
  38. static int find_file_entry_end(FILE *file, unsigned *out_name_length){
  39. fpos_t pos;
  40. unsigned i, at, e;
  41. fgetpos(file, &pos);
  42. memset(buffer, 0, 512);
  43. fread(buffer, 1, 512, file);
  44. fsetpos(file, &pos);
  45. for(i = 0; i < 256; i++){
  46. if(((signed char*)buffer)[i] > 2){
  47. at = ((unsigned char*)buffer)[i];
  48. #if 0
  49. printf("Testing %02x: %c, %c\n",
  50. at,
  51. isprint(buffer[at + i - 1]) ? buffer[at + i - 1] : '.',
  52. isprint(buffer[at + i - 0]) ? buffer[at + i - 0] : '.');
  53. #endif
  54. if(buffer[at + i - 1] == ';' && buffer[at + i - 0] == '1'){
  55. for(e = 0; e < at; e++){
  56. if(!isalnum(buffer[i + e + 1]) && !ispunct(buffer[i + e + 1])){
  57. #if 0
  58. printf("Can't be name because %02x is %02x\n",
  59. e,
  60. ((unsigned char*)buffer)[i + e + 1]);
  61. #endif
  62. goto not_upper_ascii;
  63. }
  64. }
  65. *out_name_length = at;
  66. return i + 1;
  67. }
  68. not_upper_ascii:
  69. (void)0;
  70. }
  71. }
  72. return EOF;
  73. }
  74. int main(int argc, char *argv[]){
  75. FILE *file;
  76. unsigned search_offset, i;
  77. int e;
  78. char *found, c;
  79. if(argc != 2){
  80. printf("usage: %s <track1.bin>\n", argv[0]);
  81. return -1;
  82. }
  83. if((file = fopen(argv[1], "rb")) == NULL){
  84. puts("Couldn't open file:");
  85. puts(argv[1]);
  86. return -2;
  87. }
  88. /* search for "A.BIN;1" */
  89. found = NULL;
  90. for(search_offset = 0; search_offset < 100 && found == NULL; search_offset++){
  91. fseek(file, 0xB7C0 + (search_offset * 4096), SEEK_SET);
  92. fread(buffer, 1, 4096 + sizeof(key), file);
  93. for(i = 0; i < 4096 + sizeof(key); i++){
  94. #if 0
  95. #ifndef NDEBUG
  96. unsigned e;
  97. printf("Comparing: %08X:\n", 0xB7C0 + (search_offset * 4096) + i);
  98. for(e = 0; e < sizeof(key) - 1; e++)
  99. printf("%02x", ((unsigned char*)buffer)[i + e]);
  100. putchar('\n');
  101. for(e = 0; e < sizeof(key) - 1; e++)
  102. printf("%02x", ((unsigned char*)key)[e]);
  103. putchar('\n');
  104. #endif
  105. #endif
  106. if(memcmp(buffer + i, key, sizeof(key) - 1) == 0){
  107. /* TODO: Why is this an extra -1? */
  108. i += 0xB7C0 + (search_offset * 4096) + sizeof(key) - 1 - 1;
  109. #ifndef NDEBUG
  110. printf("Found level archive header at %08x\n", i);
  111. #endif
  112. fseek(file, i, SEEK_SET);
  113. found = buffer;
  114. break;
  115. }
  116. }
  117. }
  118. if(found == NULL){
  119. puts("This doesn't look like a Clockwork Knight disc");
  120. return -3;
  121. }
  122. /* This is kinda wacky, but just dump out each byte until the name. */
  123. for(;;){
  124. e = find_file_entry_end(file, &i);
  125. if(e == EOF)
  126. break;
  127. while(e--){
  128. printf("%02X,", (unsigned char)fgetc(file));
  129. }
  130. /* putchar('\n'); */
  131. fread(buffer, 1, i, file);
  132. if(i < 3){
  133. printf("Illegal name length of %i\n", i);
  134. }
  135. else{
  136. if(buffer[i - 2] != ';' || buffer[i - 1] != '1'){
  137. puts("WARNING: Invalid string terminator");
  138. }
  139. else{
  140. buffer[i - 2] = '\0';
  141. printf("Name length: %i: %s\n", i - 2, buffer);
  142. }
  143. }
  144. }
  145. return 0;
  146. }
Télécharger Printable view

URL of this paste

Embed with JavaScript

Embed with iframe

Raw text