summaryrefslogtreecommitdiff
path: root/tools/lua/tasd-fceux-dumpscript.lua
diff options
context:
space:
mode:
authorbigbass1997 <bigbass1997.website@gmail.com>2025-04-16 21:49:22 -0500
committerbigbass1997 <bigbass1997.website@gmail.com>2025-04-16 21:49:22 -0500
commit0fb57077015001af4063a20cf8db4ce4fbf7fffc (patch)
treed053b4cf6e8933007e42bd3d69d13f46b0dda56d /tools/lua/tasd-fceux-dumpscript.lua
parentb7e8e3d85af8dd84ee9f1c523bdd3d986b64e3d3 (diff)
downloadtasd-0fb57077015001af4063a20cf8db4ce4fbf7fffc.tar.gz
tasd-0fb57077015001af4063a20cf8db4ce4fbf7fffc.zip
Move toward a release file structure. Remove outdated tools (will be readded to a different repo).
Diffstat (limited to 'tools/lua/tasd-fceux-dumpscript.lua')
-rw-r--r--tools/lua/tasd-fceux-dumpscript.lua437
1 files changed, 0 insertions, 437 deletions
diff --git a/tools/lua/tasd-fceux-dumpscript.lua b/tools/lua/tasd-fceux-dumpscript.lua
deleted file mode 100644
index 628864c..0000000
--- a/tools/lua/tasd-fceux-dumpscript.lua
+++ /dev/null
@@ -1,437 +0,0 @@
--- Copyright (c) 2022, Vi Grey
--- All rights reserved.
---
--- Redistribution and use in source and binary forms, with or without
--- modification, are permitted provided that the following conditions
--- are met:
---
--- 1. Redistributions of source code must retain the above copyright
--- notice, this list of conditions and the following disclaimer.
--- 2. Redistributions in binary form must reproduce the above copyright
--- notice, this list of conditions and the following disclaimer in the
--- documentation and/or other materials provided with the distribution.
---
--- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
--- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
--- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
--- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
--- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
--- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
--- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
--- SUCH DAMAGE.
-
-local MAGIC_NUMBER = 0x54415344
-local VERSION = 0x0001
-local G_KEYLEN = 0x02
-
-local CONSOLE_TYPE = 0x0001 -- Done
-local CONSOLE_REGION = 0x0002 -- Done
-local ROM_NAME = 0x0004 -- Done
-local EMULATOR_NAME = 0x0007 -- Done
-local DUMP_CREATED = 0x000b -- Done
-local DUMP_LAST_MODIFIED = 0x000c -- Done
-local TOTAL_FRAMES = 0x000d -- Done
-local RERECORDS = 0x000e -- Done
-local MEMORY_INIT = 0x0012 -- Done
-local GAME_IDENTIFIER = 0x0013 -- Done
-local PORT_CONTROLLER = 0x00f0 -- Done (More to handle later)
-local PORT_OVERREAD = 0x00f1 -- Done
-
-local NES_LATCH_FILTER = 0x0101 -- Done
-local NES_CLOCK_FILTER = 0x0102 -- Done
-
-local INPUT_CHUNK = 0xfe01 -- Done (More to handle later)
-local TRANSITION = 0xfe03 -- Done
-local LAG_FRAME_CHUNK = 0xfe04 -- Done
-
-local EXPERIMENTAL = 0xfffe
-
-local NES_STANDARD_CONTROLLER = 0x0101
-
-local moviePlaying = false
-local resetValueLast = 0x0000
-local afterPowerOn = false
-local lagCount = 0
-local resetTransitionPackets = ""
-local cpuCyclesLastFrame = 0
-local region = ""
-
-local tasdFile
-local lastController
-
-local nesControls = {"right", "left", "down", "up", "start", "select", "B", "A"}
-
--- Convert a non negative integer (number) to a byte string of size
--- byteLen. Returns a blank string if number is too large for byteLen
--- or if number is negative.
-function intToBytes(number, byteLen)
- local byteString = ""
- if (number < 0) then
- return byteString
- elseif (number >= (256 ^ byteLen)) then
- return byteString
- end
- for x = byteLen - 1, 0, -1 do
- byteString = byteString .. string.char(math.floor(number / 256 ^ x) % 256)
- end
- return byteString
-end
-
--- Get exponent of number
-function getExponent(number)
- return math.ceil(math.log(number + 1)/math.log(256))
-end
-
--- Create a packet byte string
-function makePacket(packetKey, packetPayload)
- local packetString = intToBytes(packetKey, G_KEYLEN)
- if packetString == "" then
- return packetString
- end
- local packetPayloadLen = string.len(packetPayload)
- local packetPayloadLenExponent = getExponent(packetPayloadLen)
- packetString = packetString .. intToBytes(packetPayloadLenExponent, 1)
- packetString = packetString .. intToBytes(packetPayloadLen, packetPayloadLenExponent)
- packetString = packetString .. packetPayload
- return packetString
-end
-
-function getConsoleTypePacket()
- local packetPayload = intToBytes(0x01, 1)
- return makePacket(CONSOLE_TYPE, packetPayload)
-end
-
-function getConsoleRegionPacket()
- local regionNumber = 3
- if (region == "NTSC") then
- regionNumber = 1
- elseif (region == "PAL") then
- regionNumber = 2
- end
- local packetPayload = intToBytes(regionNumber, 1)
- return makePacket(CONSOLE_REGION, packetPayload)
-end
-
-function getROMNamePacket()
- local packetPayload = rom.getfilename() .. ".nes"
- return makePacket(ROM_NAME, packetPayload)
-end
-
-function getMemoryInitPacket(dataType, device, required, name, data)
- local packetPayload = intToBytes(dataType, 1)
- packetPayload = packetPayload .. intToBytes(device, 2)
- packetPayload = packetPayload .. intToBytes(required, 1)
- packetPayload = packetPayload .. intToBytes(string.len(name), 1)
- packetPayload = packetPayload .. name
- packetPayload = packetPayload .. data
- return makePacket(MEMORY_INIT, packetPayload)
-end
-
-function getGameIdentifierPacket()
- local packetPayload = intToBytes(0x01, 1) -- Type is MD5
- packetPayload = packetPayload .. intToBytes(0x02, 1) -- Encoding is hex
- packetPayload = packetPayload .. intToBytes(0x00, 1) -- NLEN is 0
- packetPayload = packetPayload .. rom.gethash("md5")
- return makePacket(GAME_IDENTIFIER, packetPayload)
-end
-
-function getEmulatorNamePacket()
- local packetPayload = "fceux"
- return makePacket(EMULATOR_NAME, packetPayload)
-end
-
-function getDumpCreatedPacket(unixTimestamp)
- local packetPayload = intToBytes(unixTimestamp, 8)
- return makePacket(DUMP_CREATED, packetPayload)
-end
-
-function getDumpLastModifiedPacket(unixTimestamp)
- local packetPayload = intToBytes(unixTimestamp, 8)
- return makePacket(DUMP_LAST_MODIFIED, packetPayload)
-end
-
-function getTotalFramesPacket()
- local packetPayload = intToBytes(movie.length(), 4)
- return makePacket(TOTAL_FRAMES, packetPayload)
-end
-
-function getRerecordsPacket()
- local packetPayload = intToBytes(movie.rerecordcount(), 4)
- return makePacket(RERECORDS, packetPayload)
-end
-
--- Only supports standard controllers at the moment
-function getPortControllerPacket(playerCount, controllerType)
- local finishedPackets = ""
- for x = 1, playerCount, 1 do
- local packetPayload = intToBytes(x, 1)
- packetPayload = packetPayload .. intToBytes(controllerType, 2)
- finishedPackets = finishedPackets .. makePacket(PORT_CONTROLLER, packetPayload)
- end
- return finishedPackets
-end
-
-function getPortOverreadPacket(playerCount, controllerType)
- local finishedPackets = ""
- for x = 1, playerCount, 1 do
- local packetPayload = intToBytes(x, 1)
- packetPayload = packetPayload .. intToBytes(0x00, 1)
- finishedPackets = finishedPackets .. makePacket(PORT_OVERREAD, packetPayload)
- end
- return finishedPackets
-end
-
-
-function getNESLatchFilterPacket()
- local packetPayload = intToBytes(8000, 2)
- return makePacket(NES_LATCH_FILTER, packetPayload)
-end
-
-function getNESClockFilterPacket()
- local packetPayload = intToBytes(10, 1)
- return makePacket(NES_CLOCK_FILTER, packetPayload)
-end
-
-function getInputChunkPacket(player, inputs, controllerType)
- local payloadLen = 1
- if (controllerType == NES_STANDARD_CONTROLLER) then
- payloadLen = 1
- end
- local packetPayload = intToBytes(player, 1)
- packetPayload = packetPayload .. intToBytes(inputs, payloadLen)
- return makePacket(INPUT_CHUNK, packetPayload)
-end
-
-function getLagFrameChunkPacket()
- local packetPayload = intToBytes(movie.framecount() - lagCount, 4)
- packetPayload = packetPayload .. intToBytes(lagCount, 4)
- return makePacket(LAG_FRAME_CHUNK, packetPayload)
-end
-
-function getTransitionPacket()
- local packetPayload = intToBytes(0x01, 1)
- packetPayload = packetPayload .. intToBytes(0x00, 1)
- packetPayload = packetPayload .. intToBytes(movie.framecount(), 8)
- packetPayload = packetPayload .. intToBytes(0x01, 1)
- return makePacket(TRANSITION, packetPayload)
-end
-
-function getExperimentalPacket()
- local packetPayload = intToBytes(0x01, 1)
- return makePacket(EXPERIMENTAL, packetPayload)
-end
-
--- Get TASD file header (currently TASD\x00\x01\x02)
-function getTASDFileHeader()
- local header = intToBytes(MAGIC_NUMBER, 4)
- header = header .. intToBytes(VERSION, 2)
- header = header .. intToBytes(G_KEYLEN, 1)
- return header
-end
-
--- Begin dump by creating TASD file, writing initial data to it,
--- setting the emulator speed to maximum, and starting the movie
-function startDump()
- -- Movie has not been started since the lua script started
- setupTASDFile()
- tasdFile:write(getTASDFileHeader())
- tasdFile:write(getExperimentalPacket())
- tasdFile:write(getConsoleTypePacket())
- tasdFile:write(getROMNamePacket())
- tasdFile:write(getGameIdentifierPacket())
- tasdFile:write(getEmulatorNamePacket())
- tasdFile:write(getTotalFramesPacket())
- tasdFile:write(getRerecordsPacket())
- tasdFile:write(getPortControllerPacket(2, NES_STANDARD_CONTROLLER))
- tasdFile:write(getPortOverreadPacket(2))
- tasdFile:write(getNESLatchFilterPacket())
- tasdFile:write(getNESClockFilterPacket())
- -- Rest CPU cycle count to 0
- debugger.resetcyclescount()
- movie.playbeginning()
- emu.pause()
- -- Get CPU RAM and store it in TASD file
- storeInitialCPURAM()
- -- Get Cartridge Save RAM and store it in TASD file
- storeInitialSaveData()
- emu.unpause()
- moviePlaying = true
- print("Starting movie.\n")
- print("Replay (TASD) file will not be finished writing until the movie is " ..
- "finished.\n")
- emu.speedmode("maximum")
- setResetCallback()
-end
-
--- Set and update execution callback for when the 16-bit big-endian
--- address stored at 0xfffc-0xfffd is executed. This function is
--- called frequently because some NES cartridge mappers allow the
--- data at 0xfffc-0xfffd to be changed.
-function setResetCallback()
- resetValue = memory.readbyteunsigned(0xfffd) * 256
- resetValue = resetValue + memory.readbyteunsigned(0xfffc)
- if (resetValue ~= resetValueLast) then
- -- Value at reset vector (0xfffc-0xfffd) has changed
- unsetResetCallback()
- memory.registerexec(resetValue, detectReset)
- resetValueLast = resetValue
- end
-end
-
--- Unset the execution callback for when the 16-bit big-endian address
--- stored at 0xfffc-0xfffd is executed.
-function unsetResetCallback()
- memory.registerexec(resetValueLast, nil)
-end
-
--- Add console reset to list of reset transition packets. Ignores the
--- first detected RESET, as the initial console power-on triggers
--- this function as well
-function detectReset()
- if (afterPowerOn) then
- resetTransitionPackets = resetTransitionPackets .. getTransitionPacket()
- end
- afterPowerOn = true
-end
-
--- Some cleanup after the movie has closed, including setting the
--- emulator speed back to normal
-function finishMovie()
- emu.speedmode("normal")
- unsetResetCallback()
- moviePlaying = false
- movie.close()
- emu.registerafter(nil)
- print("Movie finished.\n")
-end
-
--- Finish writing the TASD file by writing the DUMP_CREATED,
--- DUMP_LAST_MODIFIED, and CONSOLE_REGION packets and closing the
--- TASD file.
-function finishTASDFile()
- local unixTimestamp = os.time(os.date("*t"))
- tasdFile:write(getDumpCreatedPacket(unixTimestamp))
- tasdFile:write(getDumpLastModifiedPacket(unixTimestamp))
- tasdFile:write(getConsoleRegionPacket())
- tasdFile:close()
- print("Replay (TASD) file finished.\n")
-end
-
--- Converts frame input for players 1 and 2 to an INPUT_CHUNK packet
--- and writes the INPUT_CHUNK packet to the TASD file
-function handleInputs()
- for i = 1, 2, 1 do
- local controls = joypad.get(i)
- local controllerInput = 0xff
- for x = 1, #nesControls do
- if (controls[nesControls[x]]) then
- controllerInput = bit.bxor(controllerInput, bit.lshift(1, x - 1))
- end
- end
- tasdFile:write(getInputChunkPacket(i, controllerInput, NES_STANDARD_CONTROLLER))
- end
-end
-
--- Single frame handling code. Also determines the NES console's
--- region based on the amount of CPU cycles between 2 adjacent
--- frames.
-function handleFrame()
- if (region == "") then
- local cpuCyclesNow = debugger.getcyclescount()
- local cpuCyclesDif = cpuCyclesNow - cpuCyclesLastFrame
- cyclesLastFrame = cpuCyclesNow
- if (cpuCyclesDif < 36000 and cpuCyclesDif > 29000) then
- if (cpuCyclesDif < 31000) then
- -- Region is NTSC (~29780.5 cycles per frame)
- region = "NTSC"
- elseif (cpuCyclesDif > 34000) then
- -- Region is Dendy (~35464 cycles per frame)
- region = "Dendy"
- else
- -- Region is PAL (~33247.5 cycles per frame)
- region = "PAL"
- end
- end
- else
- end
- if (emu.lagged()) then
- lagCount = lagCount + 1
- else
- if (lagCount > 0) then
- tasdFile:write(getLagFrameChunkPacket())
- end
- lagCount = 0
- handleInputs()
- end
-end
-
-
--- Create TASD File from ROM file name
-function setupTASDFile()
- local fileName = movie.name():match("(.+)%..+$")
- local tasdFileName = fileName .. ".tasd"
- tasdFile = io.open(tasdFileName, "wb+")
- print("Created replay (TASD) file " .. tasdFileName .. "\n")
-end
-
--- Store initial CPU RAM (0x0000 - 0x07FF) in TASD file
-function storeInitialCPURAM()
- print("Storing initial CPU RAM")
- local cpuRAMData = ""
- for i = 0x0000, 0x07ff, 1 do
- cpuRAMData = cpuRAMData .. string.char(memory.readbyte(i))
- end
- tasdFile:write(getMemoryInitPacket(0xff, 0x0101, 0, "", cpuRAMData))
- print("Finished storing initial CPU RAM\n")
-end
-
--- Store initial cartridge save data (0x6000 - 0x7FFF) in TASD file
-function storeInitialSaveData()
- if (bit.band(rom.readbyte(6), 2)) then
- print("Storing initial save data")
- local saveRAMData = ""
- for i = 0x6000, 0x7fff, 1 do
- saveRAMData = saveRAMData .. string.char(memory.readbyte(i))
- end
- tasdFile:write(getMemoryInitPacket(0xff, 0x0102, 0, "", saveRAMData))
- print("Finished storing initial save data\n")
- end
-end
-
--- General handler for dumping TASD file (this of this essentially as
--- the main() function)
-function gameHandler()
- if (movie.active()) then
- -- Movie is loaded/active
- if (moviePlaying == false) then
- startDump()
- else
- if (movie.framecount() > movie.length()) then
- -- End of movie
- finishTASDFile()
- finishMovie()
- print("If you wish to dump another TAS, you will need to run this script again\n")
- elseif (movie.framecount() > 1) then
- setResetCallback()
- handleFrame()
- -- End of frame
- tasdFile:write(resetTransitionPackets)
- resetTransitionPackets = ""
- end
- end
- elseif (moviePlaying == true) then
- -- Error handling movie
- finishMovie()
- print("Error handling movie. Try playing the movie again.\n")
- end
-end
-
-if (movie.active() == false) then
- print("Select a movie file\n")
-end
-
-emu.registerafter(gameHandler)
-