Super Mario 64s source code (from a leak on 4chan so be careful)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
6.0 KiB

6 years ago
  1. /*
  2. * This is a companion file for the record_demo.inc.c enhancement.
  3. *
  4. * You will need the PJ64 javascript API to get this to work, so
  5. * you should download a nightly build from here (Windows only atm):
  6. * https://www.pj64-emu.com/nightly-builds
  7. *
  8. * Place this .js file into the /Scripts/ folder in the PJ64 directory.
  9. *
  10. * In the Scripts window, double click on "RecordDemo" on the list on the left side.
  11. * When this is done, it should turn green which lets you know that it has started.
  12. *
  13. * When your demo has been recorded, it will be dumped to the newly created
  14. * /SM64_DEMOS/ folder within the PJ64 directory.
  15. */
  16. var RAM_SIZE = 4 * 1048576 // 4 MB
  17. // Get a copy of the first 4MB of memory.
  18. var RAM = mem.getblock(0x80000000, RAM_SIZE)
  19. // Create SM64_DEMOS Directory if it already doesn't exist.
  20. fs.mkdir("SM64_DEMOS/");
  21. // string "DEMORECVARS"
  22. var pattern = [0x44, 0x45, 0x4D, 0x4F, 0x52, 0x45, 0x43, 0x56, 0x41, 0x52, 0x53, 0x00]
  23. var matches = find_matches_fast(pattern)
  24. if(matches.length > 1) {
  25. console.log('Error: More than 1 instance of "DEMORECVARS" was found. Abort!')
  26. } else if(matches.length < 1) {
  27. console.log('Error: No instance of "DEMORECVARS" was found. Abort!')
  28. } else {
  29. console.clear()
  30. var demoRecVarsLocation = 0x80000000 + matches[0] + 12
  31. // Control variables addresses
  32. var gRecordingStatus_vaddr = demoRecVarsLocation + 0
  33. var gDoneDelay_vaddr = demoRecVarsLocation + 4
  34. var gNumOfRecordedInputs_vaddr = demoRecVarsLocation + 8
  35. var gRecordedInputsPtr_vaddr = demoRecVarsLocation + 12
  36. console.log('Recording variables were found at address 0x' + demoRecVarsLocation.toString(16))
  37. console.log('Initialization successful! Press L in-game to ready the demo recording before entering in a level.')
  38. // This runs every frame that is drawn.
  39. events.ondraw(function() {
  40. var gRecordingStatus = mem.u32[gRecordingStatus_vaddr]
  41. if(gRecordingStatus == 3) { // gRecordingStatus == DEMOREC_STATUS_STOPPING
  42. var gNumOfRecordedInputs = mem.u32[gNumOfRecordedInputs_vaddr]
  43. if(gNumOfRecordedInputs < 1) {
  44. console.log('Error: No inputs could be recorded!')
  45. } else {
  46. var gRecordedInputsPtr = mem.u32[gRecordedInputsPtr_vaddr]
  47. console.log('Recorded ' + gNumOfRecordedInputs + ' demo inputs.')
  48. // Grab demo data from RAM.
  49. var demo_data = mem.getblock(gRecordedInputsPtr, (gNumOfRecordedInputs + 1) * 4)
  50. // Create filename with random id added onto it.
  51. var filename = 'SM64_DEMOS/demo_' + get_random_int(0, 0xFFFFFFFF).toString(16) + '.bin'
  52. // Dump demo data to file.
  53. var file = fs.open(filename, 'wb');
  54. fs.write(file, demo_data);
  55. fs.close(file);
  56. console.log('Dumped data to file ' + filename)
  57. }
  58. // Set status to DEMOREC_STATUS_DONE
  59. mem.u32[gRecordingStatus_vaddr] = 4;
  60. // Decomp memes
  61. console.log('OK');
  62. }
  63. })
  64. }
  65. function get_random_int(min, max) {
  66. min = Math.ceil(min);
  67. max = Math.floor(max);
  68. return Math.floor(Math.random() * (max - min + 1)) + min;
  69. }
  70. /*
  71. * Finds a byte pattern that is 4-byte aligned.
  72. *
  73. * The javascript api is pretty slow when reading memory directly,
  74. * so I made this to search a copy of RAM to make things a little faster.
  75. */
  76. function find_matches_fast(pattern) {
  77. var targetLength = pattern.length
  78. var targetLengthMinusOne = targetLength - 1
  79. var matches = []
  80. var matching = 0
  81. // Increments by 8 to speed things up.
  82. for(var i = 0; i < RAM_SIZE; i += 8) {
  83. if(RAM[i] == pattern[matching])
  84. matching++
  85. else
  86. matching = 0
  87. if(matching == targetLength) {
  88. matches.push(i - targetLengthMinusOne)
  89. matching = 0
  90. }
  91. if(matching > 0) {
  92. if(RAM[i + 1] == pattern[matching])
  93. matching++
  94. else
  95. matching = 0
  96. if(matching == targetLength) {
  97. matches.push(i + 1 - targetLengthMinusOne)
  98. matching = 0
  99. }
  100. if(matching > 1) {
  101. if(RAM[i + 2] == pattern[matching])
  102. matching++
  103. else
  104. matching = 0
  105. if(matching == targetLength) {
  106. matches.push(i + 2 - targetLengthMinusOne)
  107. matching = 0
  108. }
  109. if(matching > 2) {
  110. if(RAM[i + 3] == pattern[matching])
  111. matching++
  112. else
  113. matching = 0
  114. if(matching == targetLength) {
  115. matches.push(i + 3 - targetLengthMinusOne)
  116. matching = 0
  117. }
  118. }
  119. }
  120. }
  121. if(RAM[i + 4] == pattern[matching])
  122. matching++
  123. else
  124. matching = 0
  125. if(matching == targetLength) {
  126. matches.push(i + 4 - targetLengthMinusOne)
  127. matching = 0
  128. }
  129. if(matching > 0) {
  130. if(RAM[i + 5] == pattern[matching])
  131. matching++
  132. else
  133. matching = 0
  134. if(matching == targetLength) {
  135. matches.push(i + 5 - targetLengthMinusOne)
  136. matching = 0
  137. }
  138. if(matching > 1) {
  139. if(RAM[i + 6] == pattern[matching])
  140. matching++
  141. else
  142. matching = 0
  143. if(matching == targetLength) {
  144. matches.push(i + 6 - targetLengthMinusOne)
  145. matching = 0
  146. }
  147. if(matching > 2) {
  148. if(RAM[i + 7] == pattern[matching])
  149. matching++
  150. else
  151. matching = 0
  152. if(matching == targetLength) {
  153. matches.push(i + 7 - targetLengthMinusOne)
  154. matching = 0
  155. }
  156. }
  157. }
  158. }
  159. }
  160. return matches
  161. }