--FCEUX Subtitler Express v2, by FatRatKnight -- I find the built-in subtitles for FCEUX's .fm2 is somewhat inconvenient. -- This script should make the process smoother. -- Run the script. Type whatever you like. Letters and such will appear at the -- top of the game display. The script can output a line of text that FCEUX -- can understand and translates into the appropriate subtitle. It can ouput to -- a file or to the display box -- Whatever the script outputs, you can copy that and paste it into the .fm2 -- file in question, right before where all the actual frames begin. I suggest -- WordPad to open the .fm2 file. -- If you're really that lazy, run a movie, subtitle it, then hit pageup twice. -- The movie will be stopped, but this script will replace the old subtitles -- with the new ones. It will actually create a new file appended with _S in -- case the script royally screws up somehow. -- Let a frame pass, and the text becomes translucent. This is to signify that -- the text will remain visible as an actual subtitle, but you can put in a new -- subtitle here without destroying the old one. It will just override the old -- subtitle when it's time for the new one to show up. -- Still, when it comes to translucent text, it's pretty dumb around loading -- states. Don't try to worry too much about it. -- Beware of hotkeys you set in FCEUX, or even the defaults like I, P, shift+R, -- shift+L, comma, and period. These can and will interfere with this script -- if you don't disable them. -- Please don't try to type while emulation is unpaused. This script is not -- smart enough to know how to handle unpaused typing. -- Don't let the warnings scare you. I hope the script makes subtitling easy. local BypassFrameAdvanceKey= "backslash" --Set this to the frameadvance key. local SaveToThisFile= "ConvenientSubtitles.txt" --Location to save a file local SaveOnExit= true --Dost thou wish to save when the script exits? -- List of commands: local DelChar= "backspace" -- Delete a single character local DelSub= "delete" -- Erase the whole subtitle for this frame local ShoLine= "enter" -- Prints displayed line in .fm2 readable format local SubCnt= "home" -- Counts current number of stored subtitles local PntAll= "end" -- Prints all subtitles formatted for .fm2 local SaveTxt= "pagedown" -- Saves subtitles to file local T_AutoS= "insert" -- Toggles whether the script saves on exit local SvToMov= "pageup" -- Saves subtitles to the movie file itself local WordyWords= {} local CurrentStr= "" local KeyTable= { a="a",b="b",c="c",d="d",e="e",f="f",g="g",h="h",i="i",j="j",k="k",l="l",m="m", n="n",o="o",p="p",q="q",r="r",s="s",t="t",u="u",v="v",w="w",x="x",y="y",z="z", tilde="`",minus="-",plus="=", leftbracket="[",rightbracket="]",backslash="\\", semicolon=";",quote="'", comma=",",period=".",slash="/", A="A",B="B",C="C",D="D",E="E",F="F",G="G",H="H",I="I",J="J",K="K",L="L",M="M", N="N",O="O",P="P",Q="Q",R="R",S="S",T="T",U="U",V="V",W="W",X="X",Y="Y",Z="Z", TILDE="~",MINUS="_",PLUS="+", LEFTBRACKET="{",RIGHTBRACKET="}",BACKSLASH="|", SEMICOLON=":",QUOTE="\"", COMMA="<",PERIOD=">",SLASH="?", numpad1="1",numpad2="2",numpad3="3",numpad4="4",numpad5="5", numpad6="6",numpad7="7",numpad8="8",numpad9="9",numpad0="0", space= " ", SPACE= " "} KeyTable["1"]="!" KeyTable["2"]="@" KeyTable["3"]="#" KeyTable["4"]="$" KeyTable["5"]="%" KeyTable["6"]="^" KeyTable["7"]="&" KeyTable["8"]="*" KeyTable["9"]="(" KeyTable["0"]=")" --LengthsTable= {} --for k,v in pairs(KeyTable) do -- LengthsTable[v]= gui.text(0,0,v) --end local fc, lastfc= 0, 0 --***************************************************************************** local function UpdateFC() lastfc= fc; fc= movie.framecount() end --***************************************************************************** -- Quick function. I just want it to make use of movie.framecount convenient. --***************************************************************************** local function GetSortedSubs() --***************************************************************************** -- Returns a sorted array of frame numbers for every subtitle local ReturnTbl= {} local count= 0 for frame,text in pairs(WordyWords) do count= count+1 ReturnTbl[count]= frame end table.sort(ReturnTbl) return ReturnTbl end --***************************************************************************** local function FixSubs() --***************************************************************************** -- Plants current subtitle into an array and fetches a new one. -- Should be called every time the frame count changes. Every. Single. Time. -- Can be called when the frame hasn't changed yet, if you need to. UpdateFC() if CurrentStr == "" then WordyWords[lastfc]= nil else WordyWords[lastfc]= CurrentStr end CurrentStr= WordyWords[fc] if not CurrentStr then CurrentStr= "" end end local LastSubfc= 0 local LastSub= "" --***************************************************************************** local function GhostifySubs() --***************************************************************************** -- Displays subtitles. Needs a little support from emu.registerbefore to know -- how to see an old subtitle after you decide to erase a new one. if CurrentStr ~= "" then gui.text( 3, 9,CurrentStr) elseif (fc < LastSubfc+300) and (fc > LastSubfc) then gui.opacity(.5) gui.text( 3, 9,LastSub) end gui.pixel(0,0,0) gui.opacity(1) end --***************************************************************************** local function SaveToFm2() --***************************************************************************** -- Saves to a copy of the currently playing .fm2. It will append _S to the new -- filename, so run MySweetMovie.fm2, this script makes MySweetMovie_S.fm2 -- By the way, this script tells FCEUX to stop "owning" the file as a way to -- guarantee that this script can access it without interference. FixSubs() if not movie.active() then print("Error: No movie!") return false end local OurMovie= movie.name() if string.match(OurMovie,".zip|") then print("Error: File inside a zip!") return false end movie.close() local MyFile, err= io.open(OurMovie,"r") if not MyFile then print("Error: Unable to open file for some strange reason.") print(err) return false end local MovieData= MyFile:read("*a") MyFile:close() local PreSub= string.find(MovieData,"\nsubtitle",1,true) local PreData= string.find(MovieData,"\n|%d|") -- local RemovedSubs= nil if PreSub then -- RemovedSubs= string.sub(MovieData,PreSub,PreData-1) MovieData= (string.sub(MovieData,1,PreSub-1) .. string.sub(MovieData,PreData)) PreData= PreSub end local S= GetSortedSubs() local Write= "" for i= 1, #S do Write= Write .. "subtitle " .. S[i] .. " ".. WordyWords[S[i]] .. "\n" end OurMovie= string.sub(OurMovie,1,-5) .. "_S" .. string.sub(OurMovie,-4) MyFile= io.open(OurMovie,"w") MyFile:write(string.sub(MovieData,1,PreData) .. Write .. string.sub(MovieData,PreData+1)) MyFile:close() -- print(RemovedSubs) print("Success") return true end --***************************************************************************** local function SaveToTxt() --***************************************************************************** FixSubs() local OutFile, Err= io.open(SaveToThisFile,"w") if not OutFile then print("File write fail") print(Err) return false end local Subs= GetSortedSubs() for i= 1, #Subs do OutFile:write("subtitle ".. Subs[i] .." ".. WordyWords[Subs[i]] .."\n") end OutFile:close() print("Saved",#Subs,"lines to",SaveToThisFile) return true end --***************************************************************************** local KeyFunctions= {} --***************************************************************************** -- Greatly simplifies KeyReader for me. Also, faster processing should the list -- get pretty large. -- Various odds and ends that I want done shall be done here. function KeyFunctions._DelChar() --Remove a single character if CurrentStr ~= "" then CurrentStr= string.sub(CurrentStr,1,-2) end end KeyFunctions[DelChar]= KeyFunctions._DelChar function KeyFunctions._DelSub() --Remove the whole subtitle CurrentStr= "" end KeyFunctions[DelSub]= KeyFunctions._DelSub function KeyFunctions._ShoLine() --Dump the currently displayed line print("subtitle",fc,CurrentStr) end KeyFunctions[ShoLine]= KeyFunctions._ShoLine function KeyFunctions._SubCnt() --Count subtitles local fr= GetSortedSubs() print("Subtitle count:",#fr) end KeyFunctions[SubCnt]= KeyFunctions._SubCnt function KeyFunctions._PntAll() --Dump all subtitles FixSubs() print("===========================") local S= GetSortedSubs() for i= 1, #S do print("subtitle",S[i],WordyWords[S[i]]) end end KeyFunctions[PntAll]= KeyFunctions._PntAll function KeyFunctions._SaveTxt() --Save to a file SaveToTxt() end KeyFunctions[SaveTxt]= KeyFunctions._SaveTxt function KeyFunctions._T_AutoS() --Toggle Save-On-Exit SaveOnExit= not SaveOnExit if SaveOnExit then print("SaveOnExit activated!") else print("Removed SaveOnExit.") end end KeyFunctions[T_AutoS]= KeyFunctions._T_AutoS local AmIClicked= false function KeyFunctions._SvToMov() --Saves directly to the movie itself if not movie.active() then print("No movie detected. Doing nothing...") return end if not AmIClicked then AmIClicked= true print("Movie detected. Hit",SvToMov,"again to stop movie and", "save subtitles directly into movie! Advance a frame to cancel.") else SaveToFm2() end end KeyFunctions[SvToMov]= KeyFunctions._SvToMov local lastkeys= input.get() --***************************************************************************** local function KeyReader() --***************************************************************************** if fc ~= movie.framecount() then --Egad, panic! New frame! Handle it!! FixSubs() -- Whew. Problem solved. else -- Oh? Don't panic then local keys= input.get() for k,v in pairs(keys) do if not lastkeys[k] then if k == BypassFrameAdvanceKey then --... It's a bypass. Don't process. elseif KeyFunctions[k] then KeyFunctions[k]() else local ThisKey= k if keys.shift then ThisKey= string.upper(ThisKey) else ThisKey= string.lower(ThisKey) end if KeyTable[ThisKey] then CurrentStr= CurrentStr .. KeyTable[ThisKey] end end end end GhostifySubs() lastkeys= keys end -- New frame test end --***************************************************************************** local function Ex() --***************************************************************************** if SaveOnExit then if not SaveToTxt() then print("Dumping subtitles in message box instead!") print("===========================") local S= GetSortedSubs() for i= 1, #fr do print("subtitle",S[i],WordyWords[S[i]]) end end end end --***************************************************************************** local function Bfr() --***************************************************************************** AmIClicked= false if CurrentStr ~= "" then LastSubfc= fc LastSub= CurrentStr end FixSubs() end gui.register(KeyReader) emu.registerbefore(Bfr) emu.registerexit(Ex)