Here's effectively what I have, working with DeSmuME's implementation of lua:
Download SubtitleMakerV1.luaLanguage: lua
local BIGNUM= 999999999
local BypassFrameAdvanceKey= "N"
local SubtitlesFile= "UnlikelyDuplicateFileName42.lua"
local WID= 6
local HGT= 9
local identifier
local DelChar= "backspace"
local NewLine= "enter"
local LeftArr= "left"
local RightAr= "right"
local UpArrow= "up"
local DownArr= "down"
local Del_Sub= "c+X"
local Kil_Sta= "c+G"
local Set_Sta= "c+H"
local Kil_End= "c+T"
local Set_End= "c+Y"
local SaveTxt= "c+S"
local NextSub= "numpad3"
local ResetPs= "numpad5"
local TglStat= "numpad6"
local S_Count= "home"
local T_AutoS= "insert"
local white= 0xFFFFFFFF
local red= 0xFF0000FF
local fade= 0xFFFFFF80
local f_red= 0xFF000080
function FBoxOld(x1, y1, x2, y2, color)
if (x1 == x2) and (y1 == y2) then
gui.pixel(x1,y1,color)
elseif (x1 == x2) or (y1 == y2) then
gui.line(x1,y1,x2,y2,color)
else
local temp
if x1 > x2 then
temp= x1; x1= x2; x2= temp
end
if y1 > y2 then
temp= y1; y1= y2; y2= temp
end
gui.line(x1 ,y1 ,x2-1,y1 ,color)
gui.line(x2 ,y1 ,x2 ,y2-1,color)
gui.line(x1+1,y2 ,x2 ,y2 ,color)
gui.line(x1 ,y1+1,x1 ,y2 ,color)
end
end
function FakeBox(x1, y1, x2, y2, Fill, Border)
if not Border then Border= Fill end
gui.box(x1,y1,x2,y2,Fill,0)
FBoxOld(x1,y1,x2,y2,Border)
end
local BigNum= 0xFFFFFFFF + 1
local function ColorGlitch(color)
if color >= 0x80000000 then color = color-BigNum end
return color
end
local function ProcessColors()
white= ColorGlitch(white)
red= ColorGlitch(red)
fade= ColorGlitch(fade)
f_red= ColorGlitch(f_red)
end
local box= gui.box
if stylus then
WID= 6; HGT= 9;
elseif snes9x then
WID= 4; HGT= 8; box= FakeBox
elseif vba then
WID= 4; HGT= 8;ProcessColors()
elseif FCEU then
WID= 2; HGT= 9;ProcessColors();box= FakeBox
end
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"]=")"
local S= {}
local SubIndex= 1
local Sb= {"Test",x=4,y=4,s=0,e=BIGNUM,i=1}
local ln, ch= 1, 0
S[1]= Sb
local lastkeys, keys= input.get(), input.get()
local function UpdateKeys() lastkeys= keys; keys= input.get() end
local function within(v,l,h) return (v>=l) and (v<=h) end
local function SortSubs()
table.sort(S, function (a,b) return (a.s < b.s) end)
for i= 1, #S do
S.i= i
end
end
local function GetRight(subtitle)
local x= 0
for i= 1, #subtitle do
x= math.max( x, #(subtitle[i]) )
end
return x*WID + subtitle.x + 1
end
local function GetBottom(subtitle)
return #subtitle*HGT + subtitle.y + 1
end
local function ClickedSubGeneral(x,y)
local frame= emu.framecount()
for i= 1, #S do
if ( (frame >= S[i].s)
and (frame <= S[i].e)
and (x >= S[i].x-2)
and (x <= GetRight(S[i]))
and (y >= S[i].y-2)
and (y <= GetBottom(S[i])) ) then
return i
end
end
return nil
end
local function ClickedSubFCEUX(x,y)
return ClickedSubGeneral(x,y-8)
end
if FCEU then
ClickedSub= ClickedSubFCEUX
else ClickedSub= ClickedSubGeneral end
local function DeselectSub()
end
local KeyFunctions= {}
KeyFunctions[DelChar]= function ()
if ch == 0 then
if ln > 1 then
local s= table.remove(Sb,ln)
ln= ln-1
ch= #(Sb[ln])
Sb[ln]= Sb[ln] .. s
end
else
Sb[ln]= string.sub(Sb[ln],1,ch-1) .. string.sub(Sb[ln],ch+1)
ch= ch-1
end
end
KeyFunctions[NewLine]= function ()
local s= string.sub(Sb[ln],ch+1)
Sb[ln]= string.sub(Sb[ln],1,ch)
ln= ln+1
ch= 0
table.insert(Sb,ln,s)
end
KeyFunctions[LeftArr]= function ()
ch= ch-1
if ch < 0 then
if ln > 1 then
ln= ln-1
ch= #(Sb[ln])
else
ch= 0
end
end
end
KeyFunctions[RightAr]= function ()
ch= ch+1
if ch > #(Sb[ln]) then
ln= ln+1
if ln > #Sb then
ln= #Sb
ch= #(Sb[ln])
else
ch= 0
end
end
end
KeyFunctions[UpArrow]= function ()
ln= math.max(ln-1,1)
ch= math.min(ch,#(Sb[ln]))
end
KeyFunctions[DownArr]= function ()
ln= math.min(ln+1,#Sb)
ch= math.min(ch,#(Sb[ln]))
end
KeyFunctions[Del_Sub]= function ()
if Sb then table.remove(S,Sb.i); Sb= nil end
end
KeyFunctions[Kil_Sta]= function ()
if Sb then
Sb.s= 0
print("StartPoint now zero!")
end
end
KeyFunctions[Set_Sta]= function ()
if Sb then
Sb.s= movie.framecount()
Sb.e= math.max(Sb.s,Sb.e)
print("Start:" , Sb.s)
end
end
KeyFunctions[Kil_End]= function ()
if Sb then
Sb.e= BIGNUM
print("EndPoint now super late!")
end
end
KeyFunctions[Set_End]= function ()
if Sb then
Sb.e= movie.framecount()
Sb.s= math.min(Sb.s,Sb.e)
print("End:" , Sb.e)
end
end
KeyFunctions[SaveTxt]= function ()
print("sorting...")
SortSubs()
local FileOut= io.open(SubtitlesFile,"w")
print("writing to " .. SubtitlesFile)
for i= 1, #S do
local str= string.format("S(%d,%d,%d,%d",S[i].s,S[i].e,S[i].x,S[i].y)
for j= 1, #(S[i]) do
str= str .. ",\"" .. S[i][j] .. "\""
end
str= str .. ")\n"
FileOut:write(str)
end
FileOut:close()
print("Done!")
end
KeyFunctions["leftclick"]= function ()
local ind= ClickedSub(keys.xmouse,keys.ymouse)
if Sb ~= S[ind] then
if Sb and (Sb[1] == "" and not Sb[2]) then
if ind and (ind > Sb.i) then ind= ind-1 end
table.remove(S,Sb.i)
end
Sb= S[ind]; if Sb then ln= #Sb; ch= #(Sb[ln]) end
end
end
KeyFunctions["c+leftclick"]= function ()
if Sb and (Sb[1] == "" and not Sb[2]) then
table.remove(S,Sb.i)
end
Sb={"",x=keys.xmouse,s=movie.framecount(),y=keys.ymouse,e=BIGNUM}
table.insert(S,Sb)
Sb.i= #S; ch= 0; ln= 1
end
local function KeyReader()
UpdateKeys()
for k,v in pairs(keys) do
if not lastkeys[k] then
local ThisKey= k
if lastkeys.control then ThisKey= "c+" .. ThisKey end
if ThisKey == BypassFrameAdvanceKey then
elseif KeyFunctions[ThisKey] then KeyFunctions[ThisKey]()
else
if keys.shift then ThisKey= string.upper(ThisKey)
else ThisKey= string.lower(ThisKey) end
if KeyTable[ThisKey] and Sb then
Sb[ln]= ( string.sub(Sb[ln],1,ch)
.. KeyTable[ThisKey]
.. string.sub(Sb[ln],ch+1) )
ch= ch+1
end
end
end
end
if keys.leftclick and lastkeys.leftclick and Sb then
Sb.x= Sb.x + keys.xmouse - lastkeys.xmouse
Sb.y= Sb.y + keys.ymouse - lastkeys.ymouse
end
end
local function ShowAllSubs()
for i= 1, #S do
if within(movie.framecount(), S[i].s,S[i].e) then
for j= 1, #(S[i]) do
gui.text(S[i].x, S[i].y+(j-1)*HGT, S[i][j])
end
local color= fade
if S[i] == Sb then color= white end
box(S[i].x-2, S[i].y-2, GetRight(S[i]), GetBottom(S[i]), 0, color)
elseif S[i] == Sb then
gui.opacity(.5)
for j= 1, #(S[i]) do
gui.text(S[i].x, S[i].y+(j-1)*HGT, S[i][j])
end
box(S[i].x-2, S[i].y-2, GetRight(S[i]), GetBottom(S[i]), 0, white)
gui.opacity(1)
end
end
end
local function DrawCursor()
if Sb then
local CX= Sb.x + WID*ch
local CY= Sb.y + HGT*(ln-1)
box(CX,CY,CX+WID-1,CY+HGT-1, f_red, fade)
end
end
local function Fn()
KeyReader()
ShowAllSubs()
DrawCursor()
end
local function FnFCEUX()
KeyReader()
ShowAllSubs()
DrawCursor()
gui.pixel(0,0,0)
end
if FCEU then
gui.register(FnFCEUX)
else
gui.register(Fn)
end
local NoRunWhilePausedWarning= ("Warning: This emulator does not run " ..
"lua scripts while the emulation is paused. In order for the script to " ..
"react to your typing, you must have the emulator running. " ..
"Apologies for this inconvenince."
)
if FCEU then
print("FCEU emulation detected.")
print("Warning: This emulator uses variable-width text. This script is not",
"set up for variable width. The display will incorrectly estimate the",
"length of the text. Keep this in mind as you are using this script.")
elseif vba then
print("Visual Boy Advance detected.")
print(NoRunWhilePausedWarning)
elseif stylus then
print("DeSmuME detected")
elseif snes9x then
print("Snes9x detected")
print(NoRunWhilePausedWarning)
else
print("Script not tailored for this emulator. Please edit this script and",
"manually change WID and HGT values to let the boxes match the text.",
"Additionally, I do not know of its nuances. Use with caution.")
end
print("\r\nThis script allows for arbitrary typing. Keep in mind that this",
"will likely react with the hotkeys set in the emulator. I have no way",
"of overriding the hotkeys. You are advised to make a copy of the",
"config file, then remove most or all hotkeys, especially those that",
"pertain to letters, numbers, or punctuation on the keyboard.")
print("\r\nThis can produce a file for use with the reader script.")
Oh, dear. It would appear the parser on this forum doesn't recognize backslash as an escape character for a following backslash.
There are numerous problems so far, but darn it! I can type stuff! And the stuff will show up!
It's a text editor at this point, and not a subtitles maker. But subtitles kind of requires a bit of text editing anyway. Without which, what are you going to do? Stare at the monitor and use your psychic powers?
This is an adaptation of the Subtitler script I've made for FCEUX's built-in subtitles. Seeing as I've only allowed for adding or removing the latest character in that script, that fact reflects here as well. In spite of having a cute cursor that moves left or right, typing a character always adds it to the end of the line. I'll, uh... Fix that, at some point...
There is support for backspace. It does a horrible job at deleting lines.
There is support for adding new lines with the enter key. It, too, does a horrible job, considering it erases the next line entirely when the cursor is on a previous line.
I've got pretty smart left and right arrow keys. No accompanying up and down arrow keys, though.
I chose not to search for special characters like carriage return or line feed, preferring to use separate strings instead. Seeing that this is the format I used in my previous subtitling stuff, I thought I should remain consistent as such. It's not all that easy, but I'm so far getting somewhere.
I'm just posting this to show progress. Not to show awesomeness, since I haven't reached that yet.
Edit: First piece of awesomeness at last: Movable text, spawnable text. Great neat stuffs! Ctrl+click to create a new box of text! Ctrl+X to kill the box!
Edit2: There we go. Now it saves subtitles in the format readable by my actual subtitle viewer script.
Edit3: It's now starting to feel more like a general purpose script. It has *known* support for FCEUX, Snes9x, VBA, and DeSmuME. I should work on the reader script to more appropriately match this script...