Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
-
-
()
*
/
&
&&
+
<
<=
<>
=
>
>=
abs
alert
and
backColor
BACKSPACE
beep
beepOn
bottom
buttonStyle
cast hilite
cast name
cast number
cast picture
cast size
cast text
castNum
centerStage
char
chars
charToNum
checkBoxAccess
checkBoxType
clearGlobals
clickOn
closeDA
closeResFile
closeXLib
colorDepth
colorQD
commandDown
constrainH
constraint
constrainV
contains
continue
controlDown
controller
cursor
cursor
date
delay
delete
directToStage
do
dontPassEvent
doubleClick
duration
editableText
EMPTY
ENTER
exit
exitLock
factory
factory
FALSE
field
field textAlign
field textFont
field textHeight
field textSize
field textStyle
fixStageSize
floatp
floatPrecision
foreColor
frame
framesToHMS
freeBlock
freeBytes
fullColorPermit
global
go to
go to movie
height
hilite
HMStoFrames
if
imageDirect
immediate
ink
installMenu
instance
integer
integerp
item
key
keyCode
keyDownScript
label
labelList
lastClick
lastEvent
lastKey
lastRoll
left
length
line
lineSize
locH
locV
loop
machineType
macro
marker
maxInteger
mci
memorySize
menu checkMark
menu enabled
menu name
menu script
menu:
method
mod
mouseCast
mouseChar
mouseDown
mouseDownScript
mouseH
mouseItem
mouseLine
mouseUp
mouseUpScript
mouseV
mouseWord
moveableSprite
movie
movieRate
movieTime
multiSound
not
nothing
numToChar
objectp
offset
on
on idle
on startMovie
on stepMovie
on stopMovie
open
openDA
openResFile
openXLib
optionDown
or
pathName
pattern
pause
pauseState
perFrameHook
play
play done
play movie
playAccel
preLoad
preLoadCast
printFrom
puppet
puppetPalette
puppetSound
puppetSprite
puppetTempo
puppetTransition
put
put after
put before
put into
quickTimePresent
quit
QUOTE
ramNeeded
random
repeat while
repeat with
restart
result
return
RETURN
right
rollover
romanLingo
selection
selEnd
selStart
set
setCallBack
shiftDown
showGlobals
showLocals
showResFile
showXlib
shutDown
sound
sound fadeIn
sound fadeOut
sound playFile
sound stop
sound volume
soundBusy
soundEnabled
soundLevel
sprite s intersects
sprite s within
spriteBox
sqrt
stageBottom
stageColor
stageLeft
stageRight
stageTop
starts
startTime
startTimer
stillDown
stopTime
stretch
string
stringp
switchColorDepth
symbolp
TAB
ticks
time
timeoutKeydown
timeoutLapsed
timeoutLength
timeoutMouse
timeoutPlay
timeoutScript
timer
top
trails
TRUE
type
unLoad
unLoadCast
updateStage
value
visibility
volume
when keyDown
when mouseDown
when mouseUp
when timeOut
width
word
xfactoryList
zoomBox

View File

@@ -0,0 +1,21 @@
menu: Commands A-O
alert;beep;clearGlobals;closeDA;closeResFile;closeXLib;continue;cursor;delay;delete;do;dontPassEvent;editableText;go to;go to movie;hilite;installMenu;mci;moveableSprite;nothing;open;openDA;openResFile;openXLib
menu: Commands P-Z
pause;play;play done;play movie;playAccel;preLoad;preLoadCast;printFrom;puppetPalette;puppetSound;puppetSprite;puppetTempo;puppetTransition;put ;put after;put before;put into;quit;restart;set;setCallBack;showGlobals;showLocals;showResFile;showXlib;shutDown;sound fadeIn;sound fadeOut;sound playFile;sound stop;spriteBox;startTimer;unLoad;unLoadCast;updateStage;when keyDown;when mouseDown;when mouseUp;when timeOut;zoomBox
menu: Functions A-L
abs;char;chars;charToNum;clickOn;colorQD;commandDown;constrainH;constrainV;controlDown;date;doubleClick;factory;floatp;frame;framesToHMS;freeBlock;freeBytes;HMStoFrames;integer;integerp;item;key;keyCode;label;labelList;lastClick;lastEvent;lastKey;lastRoll;length;line
menu: Functions M-Z
machineType;marker;memorySize;mouseCast;mouseChar;mouseItem;mouseLine;mouseDown;mouseWord;mouseH;mouseUp;mouseV;movie;multiSound;numToChar;objectp;offset;optionDown;pathName;pauseState;quickTimePresent;ramNeeded;random;result;rollover;selection;shiftDown;sqrt;stageBottom;stageLeft;stageRight;stageTop;stillDown;string;stringp;soundBusy;symbolp;ticks;time;value;word;xfactoryList
menu: Keywords
exit;factory;global;if;instance;macro;menu:;method;on;on idle;on startMovie;on stepMovie;on stopMovie;repeat while;repeat with;return
menu: Operators
() ;sprite s within ;sprite s intersects ; - ;not ;and ;or ;* ;/ ;mod ;+ ; - ;& ;&& ;< ;<= ;> ;>= ;<> ;contains ;starts ;=
menu: Constants
BACKSPACE ;EMPTY ;ENTER ;FALSE ;QUOTE ;RETURN ;TAB ;TRUE
menu: Properties
beepOn;buttonStyle;centerStage;checkBoxAccess;checkBoxType;colorDepth;exitLock;fixStageSize;floatPrecision;fullColorPermit;imageDirect;keyDownScript;maxInteger;mouseDownScript;mouseUpScript;perFrameHook;romanLingo;soundEnabled;soundLevel;stageColor;switchColorDepth;timeoutKeydown;timeoutLapsed;timeoutLength;timeoutMouse;timeoutPlay;timeoutScript;timer
menu: Sprite Properties
backColor;bottom;castNum;constraint;cursor;foreColor;height;immediate;ink;left;lineSize;locH;locV;movieRate;movieTime;pattern;puppet;right;startTime;stopTime;stretch;top;trails;type;visibility;volume;width
menu: Other Properties
cast hilite;cast name;cast number;cast picture;cast size;cast text;controller;directToStage;duration;field;field textAlign;field textFont;field textHeight;field textSize;field textStyle;loop;menu checkMark;menu enabled;menu name;menu script;selEnd;selStart;sound;sound volume

View File

@@ -0,0 +1,505 @@
,*
,
(
)
[
]
:
-
+
not
*
/
mod
contains
starts
and
or
=
<
<>
<=
>
>=
&
&&
then
else
the
char
word
item
line
#
true
false
return
enter
tab
backSpace
quote
empty
field
sprite
put
go
play
to
if
into
before
after
idle
startMovie
stopMovie
stepMovie
mouseUp
mouseDown
done
frame
method
of
me
off
macro
factory
while
repeat
end
with
movie
type
castNum
top
left
width
height
linesize
ink
keyDown
within
intersects
nosound
byFrame
click
clickStop
loop
noUpdate
sync
tempo
whatfits
mnew
mname
mdescribe
matframe
right
bottom
locV
locH
pattern
transition
palette
sound
editableText
moveableSprite
pause
timeout
label
puppet
immediate
forecolor
backcolor
stretch
cursor
text
hilite
cast
mverb
mdispose
mget
mput
super
noflush
xcmdglue
midiStart
midiStop
midiContinue
midiBeat
midiSong
midiSongpointer
constraint
mPerform
mActivate
mUpdate
mIdle
mMouseDown
mMouseUp
mKeyDown
mStartUp
mQuit
playRect
mEvent
on
mOpenEditor
mCloseEditor
mSetText
mGetText
noclear
mVerbDispose
startScript
version
mRespondsTo
mInstanceRespondsTo
mMessageList
chars
words
items
lines
picture
in
mPerformOther
fadeIn
fadeOut
stop
playFile
playCast
name
textStyle
textFont
textHeight
textAlign
textSize
castmembers
center
plain
bold
italic
underline
outline
shadow
condense
extend
mSetHandler
menu
menus
menuItem
menuItems
mAtTransition
mCanDoTrans
mTransDial
resource
soundEnabled
imageDirect
colorDepth
exitLock
fullColorPermit
selStart
selEnd
switchColorDepth
fixStageSize
centerStage
checkBoxAccess
checkBoxType
buttonStyle
multiSound
stageColor
beepOn
mouseDownScript
mouseUpScript
keyDownScript
timeoutScript
timer
timeoutLength
timeoutLapsed
timeoutKeydown
timeoutMouse
timeoutPlay
number
volume
checkMark
enabled
script
floatPrecision
instance
global
set
exit
when
delete
puppetSound
open
ticks
abs
length
string
charToNum
numToChar
sqrt
integerp
stringp
objectp
floatp
integer
offset
maxinteger
result
nothing
value
memorysize
freeBlock
freeBytes
commandDown
optionDown
stillDown
shiftDown
controlDown
clickOn
key
stageLeft
stageRight
stageTop
stageBottom
machineType
mouseH
mouseV
selection
pathName
labelList
pauseState
mouseLine
mouseItem
mouseWord
mouseChar
mouseCast
colorQD
doubleClick
keyCode
lastClick
lastKey
lastRoll
lastEvent
marker
rollOver
soundBusy
xfactoryList
random
constrainH
constrainV
continue
delay
openDA
installMenu
showResFile
printFrom
quit
spriteBox
startTimer
restart
shutDown
zoomBox
openXLib
closeXLib
showXLib
dontPassEvent
openResFile
closeResFile
updateStage
playAccel
immediateSprite
puppetSprite
puppetTempo
puppetTransition
puppetPalette
alert
preLoad
preLoadCast
mci
setCallBack
beep
showLocals
showGlobals
perFrameHook
long
short
abbreviated
abbrev
abbr
time
date
do
clearGlobals
unLoad
unLoadCast
trails
duration
controller
directToStage
visibility
ramNeeded
movieRate
movieTime
startTime
stopTime
romanLingo
quickTimePresent
picturep
float
soundLevel
framesToHMS
hmsToFrames
mciWait
mciBusy
closeDA
window
copyToClipBoard
pasteClipBoardInto
move
importFileInto
duplicate
findEmpty
fileName
title
visible
close
titleVisible
tell
size
add
addAt
addProp
append
deleteAt
deleteAll
deleteProp
deleteOne
getAt
getProp
getaProp
getPropAt
getPos
getOne
getLast
setAt
setProp
setaProp
count
findPos
findPosNear
sort
min
max
loc
rect
point
inflate
intersect
union
inside
map
scriptText
regPoint
bitmap
filmLoop
button
shape
digitalVideo
modified
loaded
castType
lastFrame
frameLabel
framePalette
frameTempo
frameScript
moveToFront
moveToBack
sin
cos
tan
atan
exp
log
log10
power
pi
param
paramCount
next
previous
keyUp
erase
depth
randomSeed
trace
saveMovie
clickLoc
preLoadEventAbort
updateMovieEnabled
drawRect
sourceRect
windowType
modal
windowList
itemDelimiter
last
keyUpScript
forget
down
moviePath
movieName
enterFrame
exitFrame
halt
abort
pass
property
list
linearList
propList
stage
traceLoad
traceLogFile
purgePriority
frameRate
preloadRam
pausedAtStart
video
blend
scoreColor
scriptNum
searchPaths
searchCurrentFolder
movieFileSize
movieFileFreeSize
getNthFileNameInFolder
actor
actorList
ancestor
collectChangeRects
updateRect
stepFrame
mouseTrack
birth
hitTest
mouseHitTest
ilk
void
voidp
listp
send
sendAncestor
symbolp
crop
cpuHogTicks
symbol
object
activateWindow
deactivateWindow
openWindow
closeWindow
moveWindow
zoomInWindow
zoomOutWindow
doEffects
searchPath
videoForWindowsPresent

View File

@@ -0,0 +1,693 @@
,*
,
(
)
[
]
:
-
+
not
*
/
mod
contains
starts
and
or
=
<
<>
<=
>
>=
&
&&
then
else
the
char
word
item
line
#
true
false
return
enter
tab
backSpace
quote
empty
field
sprite
put
go
play
to
if
into
before
after
idle
startMovie
stopMovie
stepMovie
mouseUp
mouseDown
done
frame
method
of
me
off
macro
factory
while
repeat
end
with
movie
type
castNum
top
left
width
height
linesize
ink
keyDown
within
intersects
nosound
byFrame
click
clickStop
loop
noUpdate
sync
tempo
whatfits
mnew
mname
mdescribe
matframe
right
bottom
locV
locH
pattern
transition
palette
sound
editableText
moveableSprite
pause
timeout
label
puppet
immediate
forecolor
backcolor
stretch
cursor
text
hilite
cast
mverb
mdispose
mget
mput
super
noflush
xcmdglue
midiStart
midiStop
midiContinue
midiBeat
midiSong
midiSongpointer
constraint
mPerform
mActivate
mUpdate
mIdle
mMouseDown
mMouseUp
mKeyDown
mStartUp
mQuit
playRect
mEvent
on
mOpenEditor
mCloseEditor
mSetText
mGetText
noclear
mVerbDispose
startScript
version
mRespondsTo
mInstanceRespondsTo
mMessageList
chars
words
items
lines
picture
in
mPerformOther
fadeIn
fadeOut
stop
playFile
playCast
name
textStyle
textFont
textHeight
textAlign
textSize
castmembers
center
plain
bold
italic
underline
outline
shadow
condense
extend
mSetHandler
menu
menus
menuItem
menuItems
mAtTransition
mCanDoTrans
mTransDial
resource
soundEnabled
imageDirect
colorDepth
exitLock
fullColorPermit
selStart
selEnd
switchColorDepth
fixStageSize
centerStage
checkBoxAccess
checkBoxType
buttonStyle
multiSound
stageColor
beepOn
mouseDownScript
mouseUpScript
keyDownScript
timeoutScript
timer
timeoutLength
timeoutLapsed
timeoutKeydown
timeoutMouse
timeoutPlay
number
volume
checkMark
enabled
script
floatPrecision
instance
global
set
exit
when
delete
puppetSound
open
ticks
abs
length
string
charToNum
numToChar
sqrt
integerp
stringp
objectp
floatp
integer
offset
maxinteger
result
nothing
value
memorysize
freeBlock
freeBytes
commandDown
optionDown
stillDown
shiftDown
controlDown
clickOn
key
stageLeft
stageRight
stageTop
stageBottom
machineType
mouseH
mouseV
selection
pathName
labelList
pauseState
mouseLine
mouseItem
mouseWord
mouseChar
mouseCast
colorQD
doubleClick
keyCode
lastClick
lastKey
lastRoll
lastEvent
marker
rollOver
soundBusy
xfactoryList
random
constrainH
constrainV
continue
delay
openDA
installMenu
showResFile
printFrom
quit
spriteBox
startTimer
restart
shutDown
zoomBox
openXLib
closeXLib
showXLib
dontPassEvent
openResFile
closeResFile
updateStage
playAccel
immediateSprite
puppetSprite
puppetTempo
puppetTransition
puppetPalette
alert
preLoad
preLoadCast
mci
setCallBack
beep
showLocals
showGlobals
perFrameHook
long
short
abbreviated
abbrev
abbr
time
date
do
clearGlobals
unLoad
unLoadCast
trails
duration
controller
directToStage
visibility
ramNeeded
movieRate
movieTime
startTime
stopTime
romanLingo
quickTimePresent
picturep
float
soundLevel
framesToHMS
hmsToFrames
mciWait
mciBusy
closeDA
window
copyToClipBoard
pasteClipBoardInto
move
importFileInto
duplicate
findEmpty
fileName
title
visible
close
titleVisible
tell
size
add
addAt
addProp
append
deleteAt
deleteAll
deleteProp
deleteOne
getAt
getProp
getaProp
getPropAt
getPos
getOne
getLast
setAt
setProp
setaProp
count
findPos
findPosNear
sort
min
max
loc
rect
point
inflate
intersect
union
inside
map
scriptText
regPoint
bitmap
filmLoop
button
shape
digitalVideo
modified
loaded
castType
lastFrame
frameLabel
framePalette
frameTempo
frameScript
moveToFront
moveToBack
sin
cos
tan
atan
exp
log
log10
power
pi
param
paramCount
next
previous
keyUp
erase
depth
randomSeed
trace
saveMovie
clickLoc
preLoadEventAbort
updateMovieEnabled
drawRect
sourceRect
windowType
modal
windowList
itemDelimiter
last
keyUpScript
forget
down
moviePath
movieName
enterFrame
exitFrame
halt
abort
pass
property
list
linearList
propList
stage
traceLoad
traceLogFile
purgePriority
frameRate
preloadRam
pausedAtStart
video
blend
scoreColor
scriptNum
searchPaths
searchCurrentFolder
movieFileSize
movieFileFreeSize
getNthFileNameInFolder
actor
actorList
ancestor
collectChangeRects
updateRect
stepFrame
mouseTrack
birth
hitTest
mouseHitTest
ilk
void
voidp
listp
send
sendAncestor
symbolp
crop
cpuHogTicks
symbol
object
doEffects
searchPath
videoForWindowsPresent
xtra
xtras
interface
member
members
castLib
castLibs
new
beginRecording
endRecording
clearFrame
updateFrame
duplicateFrame
deleteFrame
insertFrame
createName
modifyName
playing
defaultColorDepth
defaultStageRect
defaultPalette
antiAlias
remapPalettes
castCount
memberCount
minMember
maxMember
finishIdleLoad
cancelIdleLoad
idleLoadDone
idleLoadTag
idleLoadPeriod
idleLoadMode
idleReadChunkSize
idleHandlerPeriod
preLoadMode
save
richText
ole
media
boxType
border
margin
scroll
dropShadow
boxDropShadow
autoTab
textWrap
editable
sampleSize
sampleRate
channelCount
buttonType
shapeType
filled
scriptsEnabled
track
tracks
scriptType
music
timeCode
pushButton
checkBox
radioButton
oval
roundRect
score
parent
adjust
fixed
limit
digitalVideoTimeScale
videoForWindows
quickTime
fileType
case
digitalVideoType
timeScale
composite
image
textStyles
moaHandle
moaPixels
moaSound
macPICT
macGWorld
macSnd
macTEStyles
macColorTable
winDIB
winPICT
winWAVE
winPALETTE
is
otherwise
paletteMapping
updateLock
frameTransition
frameSound1
frameSound2
waitSeconds
waitClick
waitSound
waitDigitalVideo
paletteTransitionType
paletteOverTime
paletteSpeed
paletteFrames
normal
fadeToBlack
fadeToWhite
transitionType
changeArea
chunkSize
lineCount
lineHeight
linePosToLocV
locVToLinePos
charPosToLoc
locToCharPos
pageHeight
scrollTop
scrollByPage
scrollByLine
systemMac
systemWin
rainbow
grayscale
pastels
vivid
NTSC
metallic
authorMode
trackCount
trackType
trackStartTime
trackStopTime
trackEnabled
setTrackEnabled
trackNextSampleTime
trackPreviousSampleTime
trackNextKeyTime
trackPreviousKeyTime
trackText
mouseDoubleClick
mouseEnter
mouseLeave
mouseStillDown
mouseWithin
folderName
activateWindow
deactivateWindow
openWindow
closeWindow
moveWindow
resizeWindow
zoomWindow
activeWindow
frontWindow
windowPresent
deskTopRectList
scoreSelection
memberNum
castLibNum
paletteRef
keyPressed
rightMouseDown
rightMouseUp
emulateMultiButtonMouse
platform
preLoadMovie
unloadMovie
messageLock
mouseSprite
editFocusSprite
activeCast
activeCastLib
maskMember
serialNumber
userName
organizationName
runMode
appFileSpec
productName
productVersion
moaTEStyles
alignment
font
fontSize
fontStyle
wordWrap
unloadMember
preloadMember
systemWinDir4
vga

View File

@@ -0,0 +1,755 @@
,*
,
(
)
[
]
:
-
+
not
*
/
mod
contains
starts
and
or
=
<
<>
<=
>
>=
&
&&
then
else
the
char
word
item
line
#
true
false
return
enter
tab
backSpace
quote
empty
field
sprite
put
go
play
to
if
into
before
after
idle
startMovie
stopMovie
stepMovie
mouseUp
mouseDown
done
frame
method
of
me
off
macro
factory
while
repeat
end
with
movie
type
castNum
top
left
width
height
linesize
ink
keyDown
within
intersects
nosound
byFrame
click
clickStop
loop
noUpdate
sync
tempo
whatfits
mnew
mname
mdescribe
matframe
right
bottom
locV
locH
pattern
transition
palette
sound
editableText
moveableSprite
pause
timeout
label
puppet
immediate
forecolor
backcolor
stretch
cursor
text
hilite
cast
mverb
mdispose
mget
mput
super
noflush
xcmdglue
midiStart
midiStop
midiContinue
midiBeat
midiSong
midiSongpointer
constraint
mPerform
mActivate
mUpdate
mIdle
mMouseDown
mMouseUp
mKeyDown
mStartUp
mQuit
playRect
mEvent
on
mOpenEditor
mCloseEditor
mSetText
mGetText
noclear
mVerbDispose
startScript
version
mRespondsTo
mInstanceRespondsTo
mMessageList
chars
words
items
lines
picture
in
mPerformOther
fadeIn
fadeOut
stop
playFile
playCast
name
textStyle
textFont
textHeight
textAlign
textSize
castmembers
center
plain
bold
italic
underline
outline
shadow
condense
extend
mSetHandler
menu
menus
menuItem
menuItems
mAtTransition
mCanDoTrans
mTransDial
resource
soundEnabled
imageDirect
colorDepth
exitLock
fullColorPermit
selStart
selEnd
switchColorDepth
fixStageSize
centerStage
checkBoxAccess
checkBoxType
buttonStyle
multiSound
stageColor
beepOn
mouseDownScript
mouseUpScript
keyDownScript
timeoutScript
timer
timeoutLength
timeoutLapsed
timeoutKeydown
timeoutMouse
timeoutPlay
number
volume
checkMark
enabled
script
floatPrecision
instance
global
set
exit
when
delete
puppetSound
open
ticks
abs
length
string
charToNum
numToChar
sqrt
integerp
stringp
objectp
floatp
integer
offset
maxinteger
result
nothing
value
memorysize
freeBlock
freeBytes
commandDown
optionDown
stillDown
shiftDown
controlDown
clickOn
key
stageLeft
stageRight
stageTop
stageBottom
machineType
mouseH
mouseV
selection
pathName
labelList
pauseState
mouseLine
mouseItem
mouseWord
mouseChar
mouseCast
colorQD
doubleClick
keyCode
lastClick
lastKey
lastRoll
lastEvent
marker
rollOver
soundBusy
xfactoryList
random
constrainH
constrainV
continue
delay
openDA
installMenu
showResFile
printFrom
quit
spriteBox
startTimer
restart
shutDown
zoomBox
openXLib
closeXLib
showXLib
dontPassEvent
openResFile
closeResFile
updateStage
playAccel
immediateSprite
puppetSprite
puppetTempo
puppetTransition
puppetPalette
alert
preLoad
preLoadCast
mci
setCallBack
beep
showLocals
showGlobals
perFrameHook
long
short
abbreviated
abbrev
abbr
time
date
do
clearGlobals
unLoad
unLoadCast
trails
duration
controller
directToStage
visibility
ramNeeded
movieRate
movieTime
startTime
stopTime
romanLingo
quickTimePresent
picturep
float
soundLevel
framesToHMS
hmsToFrames
mciWait
mciBusy
closeDA
window
copyToClipBoard
pasteClipBoardInto
move
importFileInto
duplicate
findEmpty
fileName
title
visible
close
titleVisible
tell
size
add
addAt
addProp
append
deleteAt
deleteAll
deleteProp
deleteOne
getAt
getProp
getaProp
getPropAt
getPos
getOne
getLast
setAt
setProp
setaProp
count
findPos
findPosNear
sort
min
max
loc
rect
point
inflate
intersect
union
inside
map
scriptText
regPoint
bitmap
filmLoop
button
shape
digitalVideo
modified
loaded
castType
lastFrame
frameLabel
framePalette
frameTempo
frameScript
moveToFront
moveToBack
sin
cos
tan
atan
exp
log
log10
power
pi
param
paramCount
next
previous
keyUp
erase
depth
randomSeed
trace
saveMovie
clickLoc
preLoadEventAbort
updateMovieEnabled
drawRect
sourceRect
windowType
modal
windowList
itemDelimiter
last
keyUpScript
forget
down
moviePath
movieName
enterFrame
exitFrame
halt
abort
pass
property
list
linearList
propList
stage
traceLoad
traceLogFile
purgePriority
frameRate
preloadRam
pausedAtStart
video
blend
scoreColor
scriptNum
searchPaths
searchCurrentFolder
movieFileSize
movieFileFreeSize
getNthFileNameInFolder
actor
actorList
ancestor
collectChangeRects
updateRect
stepFrame
mouseTrack
birth
hitTest
mouseHitTest
ilk
void
voidp
listp
symbolp
crop
cpuHogTicks
symbol
object
doEffects
searchPath
videoForWindowsPresent
xtra
xtras
interface
member
members
castLib
castLibs
new
beginRecording
endRecording
clearFrame
updateFrame
duplicateFrame
deleteFrame
insertFrame
createName
modifyName
playing
defaultColorDepth
defaultStageRect
defaultPalette
antiAlias
remapPalettes
castCount
memberCount
minMember
maxMember
finishIdleLoad
cancelIdleLoad
idleLoadDone
idleLoadTag
idleLoadPeriod
idleLoadMode
idleReadChunkSize
idleHandlerPeriod
preLoadMode
save
richText
ole
media
boxType
border
margin
scroll
dropShadow
boxDropShadow
autoTab
textWrap
editable
sampleSize
sampleRate
channelCount
buttonType
shapeType
filled
scriptsEnabled
track
tracks
scriptType
music
timeCode
pushButton
checkBox
radioButton
oval
roundRect
score
parent
adjust
fixed
limit
digitalVideoTimeScale
videoForWindows
quickTime
fileType
case
digitalVideoType
timeScale
composite
image
textStyles
moaHandle
moaPixels
moaSound
macPICT
macGWorld
macSnd
macTEStyles
macColorTable
winDIB
winPICT
winWAVE
winPALETTE
is
otherwise
paletteMapping
updateLock
frameTransition
frameSound1
frameSound2
waitSeconds
waitClick
waitSound
waitDigitalVideo
waitCuePoint
paletteTransitionType
paletteOverTime
paletteSpeed
paletteFrames
normal
fadeToBlack
fadeToWhite
transitionType
changeArea
chunkSize
lineCount
lineHeight
linePosToLocV
locVToLinePos
charPosToLoc
locToCharPos
pageHeight
scrollTop
scrollByPage
scrollByLine
systemMac
systemWin
rainbow
grayscale
pastels
vivid
NTSC
metallic
authorMode
trackCount
trackType
trackStartTime
trackStopTime
trackEnabled
setTrackEnabled
trackNextSampleTime
trackPreviousSampleTime
trackNextKeyTime
trackPreviousKeyTime
trackText
prepareFrame
mouseEnter
mouseLeave
mouseStillDown
mouseWithin
folderName
activateWindow
deactivateWindow
openWindow
closeWindow
moveWindow
resizeWindow
zoomWindow
activeWindow
frontWindow
windowPresent
deskTopRectList
scoreSelection
memberNum
castLibNum
paletteRef
keyPressed
rightMouseDown
rightMouseUp
emulateMultiButtonMouse
platform
preLoadMovie
unloadMovie
messageLock
mouseSprite
editFocusSprite
activeCast
activeCastLib
maskMember
serialNumber
userName
organizationName
runMode
appFileSpec
productName
productVersion
moaTEStyles
alignment
font
fontSize
fontStyle
wordWrap
unloadMember
preloadMember
systemWinDir4
vga
SetPref
externalParamCount
externalParamName
externalParamValue
getPref
stopEvent
scriptInstanceList
currentScript
local
alertHook
testPointHook
spriteNum
mouseUpOutSide
getPropertyDescriptionList
getBehaviorDescription
runPropertyDialog
default
range
comment
format
boolean
graphic
indexcolor
rgbcolor
cuePointNames
cuePointTimes
mediaBusy
mostRecentCuePoint
currentTime
CuePassed
IsPastCuePoint
ForcePreloadCuePoints
Sound1
Sound2
Sound3
Sound4
Sound5
Sound6
Sound7
Sound8
mediaReady
call
callAncestor
sendSprite
sendAllSprites
beginSprite
endSprite
currentSpriteNum
applicationPath
space
frameReady
tweened
scriptStyles
mouseMember
netPresent
rowBytes
safePlayer
prepareMovie
send
sendAncestor
netThrottleTicks
soundKeepDevice
alphaInfo

View File

@@ -0,0 +1,806 @@
,*
.
..
,
(
)
[
]
{
}
:
-
+
not
*
/
mod
contains
starts
and
or
=
<
<>
<=
>
>=
&
&&
then
else
the
char
word
item
line
#
true
false
return
enter
tab
backSpace
quote
empty
field
sprite
put
go
play
to
if
into
before
after
idle
startMovie
stopMovie
stepMovie
mouseUp
mouseDown
done
frame
method
of
me
off
factory
while
repeat
end
with
movie
type
castNum
top
left
width
height
linesize
ink
keyDown
within
intersects
loop
sync
tempo
mnew
mname
mdescribe
right
bottom
locV
locH
pattern
transition
palette
sound
editableText
moveableSprite
pause
timeout
label
puppet
immediate
forecolor
backcolor
stretch
cursor
text
hilite
cast
mdispose
mget
mput
super
constraint
mPerform
on
version
mRespondsTo
mInstanceRespondsTo
mMessageList
chars
words
items
lines
picture
in
fadeIn
fadeOut
stop
playFile
name
textStyle
textFont
textHeight
textAlign
textSize
castmembers
center
plain
bold
italic
underline
outline
shadow
condense
extend
menu
menus
menuItem
menuItems
soundEnabled
colorDepth
exitLock
selStart
selEnd
switchColorDepth
fixStageSize
centerStage
checkBoxAccess
checkBoxType
buttonStyle
multiSound
stageColor
beepOn
mouseDownScript
mouseUpScript
keyDownScript
timeoutScript
timer
timeoutLength
timeoutLapsed
timeoutKeydown
timeoutMouse
timeoutPlay
number
volume
checkMark
enabled
script
floatPrecision
instance
global
set
exit
when
delete
puppetSound
open
ticks
abs
length
string
charToNum
numToChar
sqrt
integerp
stringp
objectp
floatp
integer
offset
maxinteger
result
nothing
value
memorysize
freeBlock
freeBytes
commandDown
optionDown
stillDown
shiftDown
controlDown
clickOn
key
stageLeft
stageRight
stageTop
stageBottom
machineType
mouseH
mouseV
selection
pathName
labelList
pauseState
mouseLine
mouseItem
mouseWord
mouseChar
mouseCast
colorQD
doubleClick
keyCode
lastClick
lastKey
lastRoll
lastEvent
marker
rollOver
soundBusy
xfactoryList
random
constrainH
constrainV
continue
delay
installMenu
showResFile
printFrom
quit
spriteBox
startTimer
restart
shutDown
zoomBox
openXLib
closeXLib
showXLib
dontPassEvent
openResFile
closeResFile
updateStage
immediateSprite
puppetSprite
puppetTempo
puppetTransition
puppetPalette
alert
preLoad
preLoadCast
mci
beep
showLocals
showGlobals
perFrameHook
long
short
abbreviated
abbrev
abbr
time
date
do
clearGlobals
unLoad
unLoadCast
trails
duration
controller
directToStage
visibility
ramNeeded
movieRate
movieTime
startTime
stopTime
romanLingo
quickTimePresent
picturep
float
soundLevel
framesToHMS
hmsToFrames
window
copyToClipBoard
pasteClipBoardInto
move
importFileInto
duplicate
findEmpty
fileName
title
visible
close
titleVisible
tell
size
add
addAt
addProp
append
deleteAt
deleteAll
deleteProp
deleteOne
getAt
getProp
getaProp
getPropAt
getPos
getOne
getLast
setAt
setProp
setaProp
count
findPos
findPosNear
sort
min
max
loc
rect
point
inflate
intersect
union
inside
map
scriptText
regPoint
bitmap
filmLoop
button
shape
digitalVideo
modified
loaded
castType
lastFrame
frameLabel
framePalette
frameTempo
frameScript
moveToFront
moveToBack
sin
cos
tan
atan
exp
log
log10
power
pi
param
paramCount
next
previous
keyUp
erase
depth
randomSeed
trace
saveMovie
clickLoc
preLoadEventAbort
updateMovieEnabled
drawRect
sourceRect
windowType
modal
windowList
itemDelimiter
last
keyUpScript
forget
down
moviePath
movieName
enterFrame
exitFrame
halt
abort
pass
property
list
linearList
propList
stage
traceLoad
traceLogFile
purgePriority
frameRate
preloadRam
pausedAtStart
video
blend
scoreColor
scriptNum
searchPaths
searchCurrentFolder
movieFileSize
movieFileFreeSize
getNthFileNameInFolder
actor
actorList
ancestor
collectChangeRects
updateRect
stepFrame
mouseTrack
birth
hitTest
mouseHitTest
ilk
void
voidp
listp
symbolp
crop
cpuHogTicks
symbol
object
doEffects
searchPath
videoForWindowsPresent
xtra
xtras
interface
member
members
castLib
castLibs
new
beginRecording
endRecording
clearFrame
updateFrame
duplicateFrame
deleteFrame
insertFrame
createName
modifyName
playing
defaultColorDepth
defaultStageRect
defaultPalette
antiAlias
remapPalettes
castCount
memberCount
minMember
maxMember
finishIdleLoad
cancelIdleLoad
idleLoadDone
idleLoadTag
idleLoadPeriod
idleLoadMode
idleReadChunkSize
idleHandlerPeriod
preLoadMode
save
richText
ole
media
boxType
border
margin
scroll
dropShadow
boxDropShadow
autoTab
textWrap
editable
sampleSize
sampleRate
channelCount
buttonType
shapeType
filled
scriptsEnabled
track
tracks
scriptType
music
timeCode
pushButton
checkBox
radioButton
oval
roundRect
score
parent
adjust
fixed
limit
digitalVideoTimeScale
videoForWindows
quickTime
fileType
case
digitalVideoType
timeScale
composite
image
textStyles
moaHandle
moaPixels
moaSound
macPICT
macGWorld
macSnd
macTEStyles
macColorTable
winDIB
winPICT
winWAVE
winPALETTE
is
otherwise
paletteMapping
updateLock
frameTransition
frameSound1
frameSound2
waitSeconds
waitClick
waitSound
waitDigitalVideo
waitCuePoint
paletteTransitionType
paletteOverTime
paletteSpeed
paletteFrames
normal
fadeToBlack
fadeToWhite
transitionType
changeArea
chunkSize
lineCount
lineHeight
linePosToLocV
locVToLinePos
charPosToLoc
locToCharPos
pageHeight
scrollTop
scrollByPage
scrollByLine
systemMac
systemWin
web216
rainbow
grayscale
pastels
vivid
NTSC
metallic
authorMode
trackCount
trackType
trackStartTime
trackStopTime
trackEnabled
setTrackEnabled
trackNextSampleTime
trackPreviousSampleTime
trackNextKeyTime
trackPreviousKeyTime
trackText
prepareFrame
mouseEnter
mouseLeave
mouseStillDown
mouseWithin
folderName
activateWindow
deactivateWindow
openWindow
closeWindow
moveWindow
resizeWindow
zoomWindow
activeWindow
frontWindow
windowPresent
deskTopRectList
scoreSelection
memberNum
castLibNum
paletteRef
keyPressed
rightMouseDown
rightMouseUp
emulateMultiButtonMouse
platform
preLoadMovie
unloadMovie
messageLock
mouseSprite
editFocusSprite
activeCast
activeCastLib
maskMember
serialNumber
userName
organizationName
runMode
appFileSpec
productName
productVersion
moaTEStyles
alignment
font
fontSize
fontStyle
wordWrap
unloadMember
preloadMember
systemWinDir4
vga
SetPref
externalParamCount
externalParamName
externalParamValue
getPref
stopEvent
scriptInstanceList
currentScript
local
alertHook
testPointHook
spriteNum
mouseUpOutSide
getPropertyDescriptionList
getBehaviorDescription
runPropertyDialog
default
range
comment
format
boolean
graphic
indexcolor
rgbcolor
cuePointNames
cuePointTimes
mediaBusy
mostRecentCuePoint
currentTime
CuePassed
IsPastCuePoint
Sound1
Sound2
Sound3
Sound4
Sound5
Sound6
Sound7
Sound8
mediaReady
call
callAncestor
sendSprite
sendAllSprites
beginSprite
endSprite
currentSpriteNum
applicationPath
space
frameReady
tweened
scriptStyles
mouseMember
netPresent
rowBytes
safePlayer
prepareMovie
send
sendAncestor
netThrottleTicks
soundKeepDevice
alphaInfo
cut
copy
paste
CastMemberProperties
quickTimeMedia
thumbnail
urlAdmin
useAlpha
alphaThreshold
rotation
skew
flipH
flipV
allowSaveLocal
allowVolumeControl
allowTransportControl
allowZooming
allowCustomCaching
allowGraphicMenu
getPropRef
getContents
setContents
globals
quad
keyboardFocusSprite
startFrame
endFrame
lineDirection
color
colorType
index
rgb
paletteIndex
red
green
blue
hexString
bgColor
bgStageColor
systemDate
year
month
day
mouseLoc
setContentsBefore
setContentsAfter
dither
mediaFormat
localString
editableMedia
soundDeviceList
soundDevice
movieCopyrightInfo
movieAboutInfo
getBehaviorTooltip
imlScrollBar
autoHilite
vectorShape
blendLevel
mapMemberToStage
mapStageToMember
xtraList
movieXtraList
milliSeconds
flash
swa
locZ
getPixel
setPixel
environment
shockMachine
paragraph
paragraphs
centerregpoint
animgif
lastChannel
bitAnd
bitOr
bitXor
bitNot
scriptList
useFastQuads
internetConnected
online
offline
unknown
applicationName
uiLanguage
productBuildVersion
activateApplication
deactivateApplication
commandLine
appMinimize
tempoScaleFactor
soundMixMedia
shockMachineVersion

View File

@@ -0,0 +1,918 @@
,*
.
..
,
(
)
[
]
{
}
:
-
+
not
*
/
mod
contains
starts
and
or
=
<
<>
<=
>
>=
&
&&
then
else
the
char
word
item
line
#
true
false
return
enter
tab
backSpace
quote
empty
field
sprite
put
to
if
into
before
after
idle
startMovie
stopMovie
stepMovie
mouseUp
mouseDown
done
frame
method
of
me
off
factory
while
repeat
end
with
top
left
width
height
keyDown
within
intersects
mnew
mname
mdescribe
right
bottom
locV
locH
sound
pause
timeout
delete
member
cast
tell
setContents
setContentsBefore
setContentsAfter
down
in
next
case
otherwise
on
global
set
exit
when
castLib
last
keyUp
enterFrame
exitFrame
activateWindow
deactivateWindow
openWindow
closeWindow
moveWindow
zoomWindow
resizeWindow
mouseEnter
mouseLeave
mouseStillDown
mouseWithin
rightMouseDown
rightMouseUp
prepareFrame
script
window
xtra
void
space
pi
getPropRef
new
property
time
date
maxinteger
systemDate
result
abs
length
string
symbol
numToChar
sqrt
integerp
stringp
vectorp
charToNum
objectp
floatp
integer
value
float
picturep
clickLoc
paramCount
param
random
floatPrecision
mouseDownScript
mouseUpScript
keyDownScript
keyUpScript
timeoutScript
short
long
abbreviated
abbrev
abbr
object
picture
number
nothing
do
abort
pass
symbolp
voidp
listp
offset
sin
cos
tan
atan
exp
log
power
list
propList
rect
point
color
rgb
paletteIndex
call
callAncestor
send
sendAncestor
bitAnd
bitOr
bitXor
bitNot
linearList
prepend
add
addAt
addProp
append
deleteAt
deleteAll
deleteProp
deleteOne
getAt
getProp
getaProp
getPropAt
getPos
getOne
getLast
setAt
setProp
setaProp
count
findPos
findPosNear
sort
min
max
ilk
duplicate
inflate
intersect
union
map
inside
getContents
instance
hexString
colorType
red
green
blue
year
month
day
mPerform
mRespondsTo
mdispose
mput
mget
super
mInstanceRespondsTo
text
fileName
ancestor
birth
actor
media
mMessageList
interface
name
mouseUpOutSide
beginSprite
endSprite
runPropertyDialog
getPropertyDescriptionList
getBehaviorDescription
getBehaviorTooltip
default
forget
romanLingo
randomSeed
itemDelimiter
globals
milliSeconds
paragraph
paragraphs
chars
words
items
lines
hilite
handler
handlers
rawNew
period
sleep
wait
notify
timeOutList
persistent
timeOutHandler
target
beep
showLocals
showGlobals
clearGlobals
memorysize
freeBlock
freeBytes
ticks
version
openXLib
closeXLib
showXLib
seconds
shockwave3d
vector
transform
sweep
protect
stats
free
file
type
folderChar
exists
volumeInfo
blockSize
freeBlocks
creator
locked
position
size
read
write
rename
exchange
getTempPath
copyTo
createFolder
deleteFolder
open
create
flush
close
folder
visible
extension
readValue
writeValue
movie
loop
go
play
label
puppet
immediate
cursor
castNum
foreColor
backColor
lineSize
stretch
ink
pattern
type
moveableSprite
editableText
palette
transition
tempo
constraint
fadeIn
fadeOut
stop
playFile
textStyle
textFont
textHeight
textAlign
textSize
castMembers
center
plain
bold
italic
underline
outline
shadow
condense
extend
menu
menus
menuItem
menuItems
soundEnabled
colorDepth
exitLock
selStart
selEnd
switchColorDepth
fixStageSize
centerStage
checkBoxAccess
checkBoxType
buttonStyle
multisound
stageColor
beepOn
timer
timeoutLength
timeoutLapsed
timeoutKeydown
timeoutMouse
timeoutPlay
volume
checkMark
enabled
puppetSound
open
commandDown
optionDown
stillDown
shiftDown
controlDown
clickOn
key
stageLeft
stageRight
stageTop
stageBottom
machineType
mouseH
mouseV
selection
pathname
labelList
pauseState
mouseLine
mouseItem
mouseWord
mouseChar
mouseCast
colorQD
doubleClick
keyCode
lastClick
lastKey
lastRoll
lastEvent
marker
rollover
soundBusy
xFactoryList
constrainH
constrainV
continue
delay
installMenu
showResFile
printFrom
quit
spriteBox
startTimer
restart
shutDown
zoomBox
dontPassEvent
openResFile
closeResFile
updateStage
immediateSprite
puppetSprite
puppetTempo
puppetTransition
puppetPalette
alert
preload
preloadCast
mci
perFrameHook
unload
unloadCast
trails
duration
controller
directToStage
visibility
ramNeeded
movieRate
movieTime
startTime
stopTime
quickTimePresent
soundLevel
framesToHms
hmsToFrames
copyToClipboard
pasteClipboardInto
move
importFileInto
findEmpty
title
visible
close
titleVisible
size
loc
scriptText
regPoint
bitmap
filmLoop
button
shape
digitalVideo
modified
loaded
castType
lastFrame
frameLabel
framePalette
frameTempo
frameScript
moveToFront
moveToBack
previous
erase
depth
trace
saveMovie
preloadEventAbort
updateMovieEnabled
drawRect
sourceRect
windowType
modal
windowList
moviePath
movieName
halt
stage
traceLoad
traceLogFile
purgePriority
frameRate
preloadRam
pausedAtStart
video
blend
scoreColor
scriptNum
searchPaths
searchCurrentFolder
movieFileSize
movieFileFreeSize
getNthFileNameInFolder
actorList
collectChangeRects
updateRect
stepFrame
mouseTrack
hitTest
mouseHitTest
crop
cpuHogTicks
doEffects
searchPath
videoForWindowsPresent
xtras
members
castLibs
beginRecording
endRecording
clearFrame
updateFrame
duplicateFrame
deleteFrame
insertFrame
createName
modifyName
playing
defaultColorDepth
defaultStageRect
defaultPalette
antialias
remapPalettes
castCount
memberCount
minMember
maxMember
finishIdleLoad
cancelIdleLoad
idleLoadDone
idleLoadTag
idleLoadPeriod
idleLoadMode
idleReadChunkSize
idleHandlerPeriod
preloadMode
save
richText
ole
boxType
border
margin
scroll
dropShadow
boxDropShadow
autoTab
textWrap
editable
sampleSize
sampleRate
channelCount
buttonType
shapeType
filled
scriptsEnabled
track
tracks
scriptType
music
timeCode
pushButton
checkBox
radioButton
oval
roundRect
score
parent
adjust
fixed
limit
digitalVideoTimeScale
videoForWindows
quickTime
fileType
digitalVideoType
timeScale
composite
image
textStyles
moaHandle
moaPixels
moaSound
macPict
macGWorld
macSnd
macTEStyles
macColorTable
winDIB
winPict
winWave
winPalette
paletteMapping
updateLock
frameTransition
frameSound1
frameSound2
waitSeconds
waitClick
waitSound
waitDigitalVideo
waitCuePoint
paletteTransitionType
paletteOverTime
paletteSpeed
paletteFrames
normal
fadeToBlack
fadeToWhite
transitionType
changeArea
chunkSize
lineCount
lineHeight
linePosToLocV
locVToLinePos
charPosToLoc
locToCharPos
pageHeight
scrollTop
scrollByPage
scrollByLine
systemMac
systemWin
web216
rainbow
grayscale
pastels
vivid
ntsc
metallic
authorMode
trackCount
trackType
trackStartTime
trackStopTime
trackEnabled
setTrackEnabled
trackNextSampleTime
trackPreviousSampleTime
trackNextKeyTime
trackPreviousKeyTime
trackText
folderName
activeWindow
frontWindow
windowPresent
desktopRectList
scoreSelection
memberNum
castLibNum
paletteRef
keyPressed
emulateMultibuttonMouse
platform
preloadMovie
unloadMovie
messageLock
mouseSprite
editFocusSprite
activeCast
activeCastLib
maskMember
serialNumber
userName
organizationName
runMode
appFileSpec
productName
productVersion
moaTEStyles
alignment
font
fontSize
fontStyle
wordWrap
unloadMember
preloadMember
systemWinDir4
vga
setPref
externalParamCount
externalParamName
externalParamValue
getPref
stopEvent
scriptInstanceList
currentScript
local
alertHook
testPointHook
spriteNum
range
comment
format
boolean
graphic
indexColor
rgbColor
cuePointNames
cuePointTimes
mediaBusy
mostRecentCuePoint
currentTime
cuePassed
isPastCuePoint
sound1
sound2
sound3
sound4
sound5
sound6
sound7
sound8
mediaReady
sendSprite
sendAllSprites
currentSpriteNum
applicationPath
frameReady
tweened
scriptStyles
mouseMember
netPresent
rowBytes
safePlayer
prepareMovie
netThrottleTicks
soundKeepDevice
alphaInfo
cut
copy
paste
castMemberProperties
quickTimeMedia
thumbnail
urlAdmin
useAlpha
alphaThreshold
rotation
skew
flipH
flipV
allowSaveLocal
allowVolumeControl
allowTransportControl
allowZooming
allowCustomCaching
allowGraphicMenu
quad
keyboardFocusSprite
startFrame
endFrame
lineDirection
index
bgColor
bgStageColor
mouseLoc
dither
mediaFormat
localString
editableMedia
soundDeviceList
soundDevice
movieCopyrightInfo
movieAboutInfo
imlScrollBar
autohilite
vectorShape
blendLevel
mapMemberToStage
mapStageToMember
xtraList
movieXtraList
flash
swa
locZ
getPixel
setPixel
environment
shockMachine
centerRegPoint
animGif
lastChannel
scriptList
useFastQuads
internetConnected
online
offline
unknown
applicationName
uiLanguage
productBuildVersion
activateApplication
deactivateApplication
commandLine
appMinimize
tempoScaleFactor
soundMixMedia
shockMachineVersion
collide
copyPixels
fill
createMatte
trimWhiteSpace
extractAlpha
setAlpha
draw
creationDate
modifiedDate
modifiedBy
comments
windowInFront
windowBehind
command
propertyDescriptionList
itemDescriptionList
granularity
imageCompression
imageQuality
movieSetting
standard
jpeg
gif
movieImageCompression
movieImageQuality
linked
transparent
reverse
ghost
notCopy
notTransparent
notReverse
notGhost
matte
mask
addPin
subtractPin
backgroundTransparent
lightest
subtract
darkest
lighten
darken
maskImage
maskOffset
createMask
movieFileVersion
editShortcutsEnabled
inlineImeEnabled
linkAs
flushInputEvents
setScriptList
isOKToAttach
osLanguage
pointToChar
pointToWord
pointToLine
pointToItem
pointToParagraph
selectedText
markerlist
enableInkmodeLimitations
_soundXtra
helpTopic
disableImagingTransformation

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,257 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_BUILTINS_H
#define DIRECTOR_LINGO_BUILTINS_H
namespace Director {
namespace LB {
// builtin functions
void b_abs(int nargs);
void b_atan(int nargs);
void b_cos(int nargs);
void b_exp(int nargs);
void b_float(int nargs);
void b_integer(int nargs);
void b_log(int nargs);
void b_pi(int nargs);
void b_power(int nargs);
void b_random(int nargs);
void b_sin(int nargs);
void b_sqrt(int nargs);
void b_tan(int nargs);
void b_void(int nargs);
void b_chars(int nargs);
void b_charToNum(int nargs);
void b_length(int nargs);
void b_numToChar(int nargs);
void b_offset(int nargs);
void b_string(int nargs);
void b_add(int nargs);
void b_addAt(int nargs);
void b_addProp(int nargs);
void b_append(int nargs);
void b_count(int nargs);
void b_deleteAt(int nargs);
void b_deleteOne(int nargs);
void b_deleteProp(int nargs);
void b_duplicateList(int nargs);
void b_findPos(int nargs);
void b_findPosNear(int nargs);
void b_getaProp(int nargs);
void b_getAt(int nargs);
void b_getLast(int nargs);
void b_getOne(int nargs);
void b_getPos(int nargs);
void b_getProp(int nargs);
void b_getPropAt(int nargs);
void b_list(int nargs);
void b_listP(int nargs);
void b_max(int nargs);
void b_min(int nargs);
void b_setaProp(int nargs);
void b_setAt(int nargs);
void b_setProp(int nargs);
void b_sort(int nargs);
void b_factory(int nargs);
void b_floatP(int nargs);
void b_ilk(int nargs);
void b_integerp(int nargs);
void b_objectp(int nargs);
void b_pictureP(int nargs);
void b_stringp(int nargs);
void b_symbol(int nargs);
void b_symbolp(int nargs);
void b_voidP(int nargs);
void b_alert(int nargs);
void b_clearGlobals(int nargs);
void b_cursor(int nargs);
void b_framesToHMS(int nargs);
void b_HMStoFrames(int nargs);
void b_param(int nargs);
void b_printFrom(int nargs);
void b_put(int nargs);
void b_setPref(int nargs);
void b_showGlobals(int nargs);
void b_showLocals(int nargs);
void b_value(int nargs);
void b_constrainH(int nargs);
void b_constrainV(int nargs);
void b_copyToClipBoard(int nargs);
void b_duplicate(int nargs);
void b_editableText(int nargs);
void b_erase(int nargs);
void b_findEmpty(int nargs);
void b_importFileInto(int nargs);
void b_installMenu(int nargs);
void b_label(int nargs);
void b_marker(int nargs);
void b_move(int nargs);
void b_moveableSprite(int nargs);
void b_pasteClipBoardInto(int nargs);
void b_puppetPalette(int nargs);
void b_puppetSound(int nargs);
void b_puppetSprite(int nargs);
void b_puppetTempo(int nargs);
void b_puppetTransition(int nargs);
void b_ramNeeded(int nargs);
void b_rollOver(int nargs);
void b_sendAllSprites(int nargs);
void b_sendSprite(int nargs);
void b_spriteBox(int nargs);
void b_unLoad(int nargs);
void b_unLoadCast(int nargs);
void b_unLoadMovie(int nargs);
void b_updateStage(int nargs);
void b_zoomBox(int nargs);
void b_immediateSprite(int nargs);
void b_clearFrame(int nargs);
void b_deleteFrame(int nargs);
void b_duplicateFrame(int nargs);
void b_insertFrame(int nargs);
void b_updateFrame(int nargs);
void b_abort(int nargs);
void b_call(int nargs);
void b_callAncestor(int nargs);
void b_cancelIdleLoad(int nargs);
void b_continue(int nargs);
void b_dontPassEvent(int nargs);
void b_delay(int nargs);
void b_do(int nargs);
void b_finishIdleLoad(int nargs);
void b_go(int nargs);
void b_halt(int nargs);
void b_idleLoadDone(int nargs);
void b_nothing(int nargs);
void b_pass(int nargs);
void b_pause(int nargs);
void b_play(int nargs);
void b_playAccel(int nargs);
void b_preLoad(int nargs);
void b_preLoadCast(int nargs);
void b_preLoadMovie(int nargs);
void b_quit(int nargs);
void b_restart(int nargs);
void b_shutDown(int nargs);
void b_startTimer(int nargs);
void b_stopEvent(int nargs);
void b_return(int nargs);
void b_closeDA(int nargs);
void b_closeResFile(int nargs);
void b_closeXlib(int nargs);
void b_getNthFileNameInFolder(int nargs);
void b_open(int nargs);
void b_openDA(int nargs);
void b_openResFile(int nargs);
void b_openXlib(int nargs);
void b_setCallBack(int nargs);
void b_save(int nargs);
void b_saveMovie(int nargs);
void b_showResFile(int nargs);
void b_showXlib(int nargs);
void b_xFactoryList(int nargs);
void b_xtra(int nargs);
void b_point(int nargs);
void b_inflate(int nargs);
void b_inside(int nargs);
void b_intersect(int nargs);
void b_map(int nargs);
void b_offsetRect(int nargs);
void b_rect(int nargs);
void b_union(int nargs);
void b_beep(int nargs);
void b_mci(int nargs);
void b_mciwait(int nargs);
void b_sound(int nargs);
void b_soundBusy(int nargs);
void b_backspace(int nargs);
void b_empty(int nargs);
void b_enter(int nargs);
void b_false(int nargs);
void b_quote(int nargs);
void b_returnconst(int nargs);
void b_tab(int nargs);
void b_true(int nargs);
void b_version(int nargs);
void b_cast(int nargs);
void b_castLib(int nargs);
void b_member(int nargs);
void b_script(int nargs);
void b_sprite(int nargs);
void b_window(int nargs);
void b_windowPresent(int nargs);
void b_charPosToLoc(int nargs);
void b_linePosToLocV(int nargs);
void b_locToCharPos(int nargs);
void b_locVToLinePos(int nargs);
void b_scrollByLine(int nargs);
void b_scrollByPage(int nargs);
void b_lineHeight(int nargs);
void b_numberofchars(int nargs);
void b_numberofitems(int nargs);
void b_numberoflines(int nargs);
void b_numberofwords(int nargs);
void b_trackCount(int nargs);
void b_trackStartTime(int nargs);
void b_trackStopTime(int nargs);
void b_trackType(int nargs);
void b_isPastCuePoint(int nargs);
void b_beginRecording(int nargs);
void b_endRecording(int nargs);
void b_scummvmassert(int nargs);
void b_scummvmassertequal(int nargs);
void b_scummvmNoFatalError(int nargs);
// XCMD/XFCN (HyperCard), normally exposed
void b_getVolumes(int nargs);
void b_externalParamCount(int nargs); // Shockwave D6
void b_externalParamName(int nargs); // Shockwave D6
void b_externalParamValue(int nargs); // Shockwave D6
void b_frameReady(int nargs); // Shockwave D6
void b_getPref(int nargs); // Shockwave D6
void b_netPresent(int nargs); // Shockwave D6
} // End of namespace LB
} // End of namespace Director
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_LINGO_BYTECODE_H
#define DIRECTOR_LINGO_LINGO_BYTECODE_H
namespace Director {
typedef void (*inst)(void);
struct LingoV4Bytecode {
const uint8 opcode;
const inst func;
const char *proto;
};
enum TheEntityArgsType {
kTEANOArgs = 0,
kTEAItemId = 1,
kTEAString,
kTEAMenuId,
kTEAMenuIdItemId,
kTEAChunk
};
struct LingoV4TheEntity {
const uint8 bank;
const uint8 firstArg;
const int entity;
const int field;
const bool writable;
const TheEntityArgsType type;
};
} // End of namespace Director
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,180 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_LINGO_CODE_H
#define DIRECTOR_LINGO_LINGO_CODE_H
namespace Director {
namespace LC {
void c_xpop();
Datum mapBinaryOp(Datum (*func)(Datum &, Datum &), Datum &d1, Datum &d2);
Datum addData(Datum &d1, Datum &d2);
void c_add();
Datum subData(Datum &d1, Datum &d2);
void c_sub();
Datum mulData(Datum &d1, Datum &d2);
void c_mul();
Datum divData(Datum &d1, Datum &d2);
void c_div();
Datum modData(Datum &d1, Datum &d2);
void c_mod();
Datum negateData(Datum &d1);
void c_negate();
void c_and();
void c_or();
void c_not();
void c_ampersand();
void c_putafter();
void c_putbefore();
void c_concat();
void c_contains();
void c_starts();
void c_intersects();
void c_within();
Datum chunkRef(ChunkType type, int startChunk, int endChunk, const Datum &src);
Datum lastChunk(ChunkType type, const Datum &src);
Datum readChunkRef(const Datum &src);
void c_of();
void c_charToOfRef();
void c_charToOf();
void c_itemToOfRef();
void c_itemToOf();
void c_lineToOfRef();
void c_lineToOf();
void c_wordToOfRef();
void c_wordToOf();
void c_constpush();
void c_intpush();
void c_voidpush();
void c_floatpush();
void c_stringpush();
void c_symbolpush();
void c_namepush();
void c_varrefpush();
void c_globalrefpush();
void c_localrefpush();
void c_proprefpush();
void c_varpush();
void c_globalinit();
void c_globalpush();
void c_localpush();
void c_proppush();
void c_argcpush();
void c_argcnoretpush();
void c_arraypush();
void c_proparraypush();
void c_stackpeek();
void c_stackdrop();
void c_assign();
bool verify(const Symbol &s);
void c_swap();
void c_theentitypush();
void c_themenuentitypush();
void c_theentityassign();
void c_objectproppush();
void c_objectpropassign();
void c_whencode();
void c_tell();
void c_telldone();
Datum compareArrays(Datum (*compareFunc)(Datum, Datum), Datum d1, Datum d2, bool location = false, bool value = false);
Datum eqData(Datum d1, Datum d2);
Datum eqDataStrict(Datum d1, Datum d2);
void c_eq();
Datum neqData(Datum d1, Datum d2);
void c_neq();
Datum gtData(Datum d1, Datum d2);
void c_gt();
Datum ltData(Datum d1, Datum d2);
void c_lt();
Datum geData(Datum d1, Datum d2);
void c_ge();
Datum leData(Datum d1, Datum d2);
void c_le();
void c_jump();
void c_jumpifz();
void c_callcmd();
void c_callfunc();
void call(const Symbol &targetSym, int nargs, bool allowRetVal);
void call(const Common::String &name, int nargs, bool allowRetVal);
void c_procret();
void procret();
void c_mci();
void c_mciwait();
void c_open();
void c_delete();
void c_hilite();
void c_field();
void c_fieldref();
// custom instructions for testing
void c_asserterror();
void c_asserterrordone();
// stubs for unknown instructions
void cb_unk();
void cb_unk1();
void cb_unk2();
// bytecode-related instructions
void cb_call();
void cb_delete();
void cb_hilite();
void cb_globalassign();
void cb_globalpush();
void cb_list();
void cb_localcall();
void cb_objectcall();
void cb_objectfieldassign();
void cb_objectfieldpush();
void cb_varrefpush();
void cb_theassign();
void cb_theassign2();
void cb_thepush();
void cb_thepush2();
void cb_proplist();
void cb_varassign();
void cb_varpush();
void cb_v4assign();
void cb_v4assign2();
void cb_v4theentitypush();
void cb_v4theentitynamepush();
void cb_v4theentityassign();
void cb_zeropush();
} // End of namespace LC
} // End of namespace Director
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_LINGO_CODEGEN_H
#define DIRECTOR_LINGO_LINGO_CODEGEN_H
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-ast.h"
namespace Director {
class LingoCompiler : NodeVisitor {
public:
LingoCompiler();
virtual ~LingoCompiler() {}
ScriptContext *compileAnonymous(const Common::U32String &code, uint32 preprocFlags = 0);
ScriptContext *compileLingo(const Common::U32String &code, LingoArchive *archive, ScriptType type, CastMemberID id, const Common::String &scriptName, bool anonyomous = false, uint32 preprocFlags = kLPPNone);
ScriptContext *compileLingoV4(Common::SeekableReadStreamEndian &stream, uint16 lctxIndex, LingoArchive *archive, const Common::String &archName, uint16 version);
int code1(inst code) { _currentAssembly->push_back(code); return _currentAssembly->size() - 1; }
int code2(inst code_1, inst code_2) { int o = code1(code_1); code1(code_2); return o; }
int code3(inst code_1, inst code_2, inst code_3) { int o = code1(code_1); code1(code_2); code1(code_3); return o; }
int code4(inst code_1, inst code_2, inst code_3, inst code_4) { int o = code1(code_1); code1(code_2); code1(code_3); code1(code_4); return o; }
int codeCmd(const Common::String &s, int numpar);
int codeFloat(double f);
int codeFunc(const Common::String &s, int numpar);
int codeInt(int val);
int codeString(const char *s);
void codeVarSet(const Common::String &name);
void codeVarRef(const Common::String &name);
void codeVarGet(const Common::String &name);
int getTheFieldID(int entity, const Common::String &field, bool silent = false);
void registerFactory(Common::String &s);
void registerMethodVar(const Common::String &name, VarType type = kVarGeneric);
void updateLoopJumps(uint nextTargetPos, uint exitTargetPos);
LingoArchive *_assemblyArchive;
ScriptContext *_assemblyContext;
Common::SharedPtr<Node> _assemblyAST;
int32 _assemblyId;
ScriptData *_currentAssembly;
bool _indef;
uint _linenumber;
uint _colnumber;
uint _bytenumber;
const char *_lines[3];
bool _inFactory;
LoopNode *_currentLoop;
bool _refMode;
Common::HashMap<Common::String, VarType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> *_methodVars;
bool _hadError;
public:
virtual bool visitScriptNode(ScriptNode *node);
virtual bool visitFactoryNode(FactoryNode *node);
virtual bool visitHandlerNode(HandlerNode *node);
virtual bool visitCmdNode(CmdNode *node);
virtual bool visitPutIntoNode(PutIntoNode *node);
virtual bool visitPutAfterNode(PutAfterNode *node);
virtual bool visitPutBeforeNode(PutBeforeNode *node);
virtual bool visitSetNode(SetNode *node);
virtual bool visitGlobalNode(GlobalNode *node);
virtual bool visitPropertyNode(PropertyNode *node);
virtual bool visitInstanceNode(InstanceNode *node);
virtual bool visitIfStmtNode(IfStmtNode *node);
virtual bool visitIfElseStmtNode(IfElseStmtNode *node);
virtual bool visitRepeatWhileNode(RepeatWhileNode *node);
virtual bool visitRepeatWithToNode(RepeatWithToNode *node);
virtual bool visitRepeatWithInNode(RepeatWithInNode *node);
virtual bool visitNextRepeatNode(NextRepeatNode *node);
virtual bool visitExitRepeatNode(ExitRepeatNode *node);
virtual bool visitExitNode(ExitNode *node);
virtual bool visitReturnNode(ReturnNode *node);
virtual bool visitTellNode(TellNode *node);
virtual bool visitWhenNode(WhenNode *node);
virtual bool visitDeleteNode(DeleteNode *node);
virtual bool visitHiliteNode(HiliteNode *node);
virtual bool visitAssertErrorNode(AssertErrorNode *node);
virtual bool visitIntNode(IntNode *node);
virtual bool visitFloatNode(FloatNode *node);
virtual bool visitSymbolNode(SymbolNode *node);
virtual bool visitStringNode(StringNode *node);
virtual bool visitListNode(ListNode *node);
virtual bool visitPropListNode(PropListNode *node);
virtual bool visitPropPairNode(PropPairNode *node);
virtual bool visitFuncNode(FuncNode *node);
virtual bool visitVarNode(VarNode *node);
virtual bool visitParensNode(ParensNode *node);
virtual bool visitUnaryOpNode(UnaryOpNode *node);
virtual bool visitBinaryOpNode(BinaryOpNode *node);
virtual bool visitFrameNode(FrameNode *node);
virtual bool visitMovieNode(MovieNode *node);
virtual bool visitIntersectsNode(IntersectsNode *node);
virtual bool visitWithinNode(WithinNode *node);
virtual bool visitTheNode(TheNode *node);
virtual bool visitTheOfNode(TheOfNode *node);
virtual bool visitTheNumberOfNode(TheNumberOfNode *node);
virtual bool visitTheLastNode(TheLastNode *node);
virtual bool visitTheDateTimeNode(TheDateTimeNode *node);
virtual bool visitMenuNode(MenuNode *node);
virtual bool visitMenuItemNode(MenuItemNode *node);
virtual bool visitSoundNode(SoundNode *node);
virtual bool visitSpriteNode(SpriteNode *node);
virtual bool visitChunkExprNode(ChunkExprNode *node);
private:
int parse(const char *code);
public:
// lingo-preprocessor.cpp
Common::U32String codePreprocessor(const Common::U32String &code, LingoArchive *archive, ScriptType type, CastMemberID id, uint32 flags);
MethodHash prescanMethods(const Common::U32String &code);
// lingo-patcher.cpp
Common::U32String patchLingoCode(const Common::U32String &line, LingoArchive *archive, ScriptType type, CastMemberID id, int linenumber);
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,903 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "director/director.h"
#include "director/debugger.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-builtins.h"
#include "director/lingo/lingo-code.h"
#include "director/lingo/lingo-object.h"
#include "director/cast.h"
#include "director/castmember/castmember.h"
#include "director/channel.h"
#include "director/frame.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/sprite.h"
#include "director/types.h"
#include "director/window.h"
namespace Director {
struct EventHandlerType {
LEvent handler;
const char *name;
} static const eventHandlerDescs[] = {
{ kEventPrepareMovie, "prepareMovie" }, // D6
{ kEventStartMovie, "startMovie" }, // D3
{ kEventStepMovie, "stepMovie" }, // D3
{ kEventStopMovie, "stopMovie" }, // D3
{ kEventBeginSprite, "beginSprite" }, // D6
{ kEventEndSprite, "endSprite" }, // D6
{ kEventEnterFrame, "enterFrame" }, // D4
{ kEventPrepareFrame, "prepareFrame" }, // D6
{ kEventIdle, "idle" }, // D3
{ kEventStepFrame, "stepFrame"}, // D5
{ kEventExitFrame, "exitFrame" }, // D4
{ kEventActivateWindow, "activateWindow" }, // D5
{ kEventDeactivateWindow, "deactivateWindow" }, // D5
{ kEventMoveWindow, "moveWindow" }, // D5
{ kEventResizeWindow, "resizeWindow" }, // D5
{ kEventOpenWindow, "openWindow" }, // D5
{ kEventCloseWindow, "closeWindow" }, // D5
{ kEventZoomWindow, "zoomWindow" }, // D5
{ kEventKeyUp, "keyUp" }, // D4
{ kEventKeyDown, "keyDown" }, // D2 w D4 (as when from D2)
{ kEventMouseUp, "mouseUp" }, // D2 w D3
{ kEventMouseDown, "mouseDown" }, // D2 w D3
{ kEventRightMouseDown, "rightMouseDown" }, // D5
{ kEventRightMouseUp, "rightMouseUp" }, // D5
{ kEventMouseEnter, "mouseEnter" }, // D6, present in D5
{ kEventMouseLeave, "mouseLeave" }, // D6, present in D5
{ kEventMouseUpOutSide, "mouseUpOutSide" }, // D6
{ kEventMouseWithin, "mouseWithin" }, // D6, present in D5
{ kEventTimeout, "timeout" }, // D2 as when
{ kEventCuePassed, "cuePassed" }, // D6
{ kEventStartUp, "startUp" },
{ kEventGetBehaviorDescription, "getBehaviorDescription" }, // D6
{ kEventGetPropertyDescriptionList, "getPropertyDescriptionList" }, // D6
{ kEventRunPropertyDialog, "runPropertyDialog" }, // D6
{ kEventGeneric, "scummvm_generic" },
{ kEventNone, nullptr }
};
void Lingo::initEventHandlerTypes() {
for (const EventHandlerType *t = &eventHandlerDescs[0]; t->handler != kEventNone; ++t) {
_eventHandlerTypeIds[t->name] = t->handler;
_eventHandlerTypes[t->handler] = t->name;
}
_eventHandlerTypes[kEventNone] = 0;
}
ScriptType Lingo::event2script(LEvent ev) {
if (_vm->getVersion() < 400) {
switch (ev) {
//case kEventStartMovie: // We are precompiling it now
// return kMovieScript;
case kEventExitFrame:
return kScoreScript;
default:
return kNoneScript;
}
}
return kNoneScript;
}
void Movie::setPrimaryEventHandler(LEvent event, const Common::String &code) {
debugC(3, kDebugLingoExec, "setting primary event handler (%s)", _lingo->_eventHandlerTypes[event]);
LingoArchive *mainArchive = getMainLingoArch();
mainArchive->primaryEventHandlers[event] = code;
mainArchive->replaceCode(code, kEventScript, event);
}
void Movie::resolveScriptEvent(LingoEvent &event) {
// Resolve the script details of an event.
// This must be done at execution time, as it relies on
// e.g. the current frame, the current arrangement of sprites...
uint16 spriteId = 0;
if (event.mousePos != Common::Point(-1, -1)) {
// Fetch the sprite underneath the mouse cursor.
// D3 doesn't have both mouse up and down.
// But we still want to know if the mouse is down for press effects.
// Since we don't have mouse up and down before D3, then we use ActiveSprite
if (g_director->getVersion() < 400)
spriteId = _score->getActiveSpriteIDFromPos(event.mousePos);
else
spriteId = _score->getMouseSpriteIDFromPos(event.mousePos);
if (event.event == kEventMouseDown || event.event == kEventRightMouseDown)
_lastClickedSpriteId = _score->getActiveSpriteIDFromPos(event.mousePos); // the clickOn
}
// Very occasionally, we want to specify an event with a channel ID
// rather than infer it from the position. Allow it to override.
if (event.channelId == 0) {
event.channelId = spriteId;
}
// mouseDown/mouseUp events will have one of each of the source types queued.
// run these steps at the very beginning (i.e. before the first source type).
if (event.eventHandlerSourceType == kPrimaryHandler) {
if ((event.event == kEventMouseDown) || (event.event == kEventRightMouseDown)) {
if (!event.channelId && _isBeepOn) {
g_lingo->func_beep(1);
}
if (event.channelId > 0) {
if (_score->_channels[event.channelId]->_sprite->shouldHilite()) {
_currentHiliteChannelId = event.channelId;
g_director->_wm->_hilitingWidget = true;
g_director->getCurrentWindow()->setDirty(true);
g_director->getCurrentWindow()->addDirtyRect(_score->_channels[_currentHiliteChannelId]->getBbox());
}
CastMember *cast = getCastMember(_score->_channels[event.channelId]->_sprite->_castId);
if (cast && cast->_type == kCastButton)
_mouseDownWasInButton = true;
if (_score->_channels[event.channelId]->_sprite->_moveable) {
_draggingSpriteOffset = _score->_channels[event.channelId]->getPosition() - event.mousePos;
_currentDraggedChannel = _score->_channels[event.channelId];
}
// In the case of clicking the mouse, it is possible for a mouseDown action to
// change the cast member underneath. on mouseUp should always load the cast
// script for the original cast member, not the new one.
_currentMouseDownCastID = _score->_channels[event.channelId]->_sprite->_castId;
_currentMouseDownSpriteScriptID = _score->_channels[event.channelId]->_sprite->_scriptId;
_currentMouseDownSpriteImmediate = _score->_channels[event.channelId]->_sprite->_immediate;
} else {
_currentHiliteChannelId = 0;
_mouseDownWasInButton = false;
_draggingSpriteOffset = Common::Point(0, 0);
_currentDraggedChannel = nullptr;
_currentMouseDownCastID = CastMemberID();
_currentMouseDownSpriteScriptID = CastMemberID();
_currentMouseDownSpriteImmediate = false;
}
} else if ((event.event == kEventMouseUp) || (event.event == kEventRightMouseUp)) {
if (_currentHiliteChannelId && _score->_channels[_currentHiliteChannelId]) {
g_director->getCurrentWindow()->setDirty(true);
g_director->getCurrentWindow()->addDirtyRect(_score->_channels[_currentHiliteChannelId]->getBbox());
}
g_director->_wm->_hilitingWidget = false;
_currentDraggedChannel = nullptr;
// If this is a button cast member, and the last mouse down event was in a button
// (any button), flip this button's hilite flag.
// Now you might think, "Wait, we don't flip this flag in the mouseDown event.
// And why any button??? This doesn't make any sense."
// No, it doesn't make sense, but it's what Director does.
if (_mouseDownWasInButton && event.channelId) {
CastMember *cast = getCastMember(_score->_channels[event.channelId]->_sprite->_castId);
if (cast && cast->_type == kCastButton)
cast->_hilite = !cast->_hilite;
}
_currentHiliteChannelId = 0;
_mouseDownWasInButton = false;
g_director->loadSlowdownCooloff();
}
}
switch (event.eventHandlerSourceType) {
case kPrimaryHandler:
// Run the primary event handler.
// Note that this isn't a "real" cast member ID, it's just the enum
// of the type of event, so it can be crammed into the script context
// index. kEventScript is a script type reserved for the primary event
// handlers (e.g. the mouseDownScript, the mouseUpScript), so there will
// be no collision with script cast members like ScoreScripts.
{
CastMemberID scriptId(event.event, DEFAULT_CAST_LIB);
if (getScriptContext(kEventScript, scriptId)) {
event.event = kEventGeneric;
event.scriptType = kEventScript;
event.scriptId = scriptId;
}
}
break;
/* When the mouseDown or mouseUp occurs over a sprite, the message
* goes first to the sprite script, then to the script of the cast
* member, to the frame script and finally to the movie scripts.
*
* When the mouseDown or mouseUp doesn't occur over a sprite, the
* message goes to the frame script and then to the movie script.
*
* When more than one movie script [...]
* [D4 docs] */
case kSpriteHandler:
{
CastMemberID scriptId;
bool immediate = false;
Common::String initializerParams;
// mouseUp events seem to check the frame script ID from the original mouseDown event
// In Director 5 and above, we always generate event for the actual sprite under the mouse
if (((event.event == kEventMouseUp) || (event.event == kEventRightMouseUp)) && _vm->getVersion() < 500) {
scriptId = _currentMouseDownSpriteScriptID;
immediate = _currentMouseDownSpriteImmediate;
} else {
if (!event.channelId)
return;
Frame *currentFrame = _score->_currentFrame;
assert(currentFrame != nullptr);
Sprite *sprite = _score->getSpriteById(event.channelId);
if (!sprite)
return;
if (_vm->getVersion() >= 600) {
if (event.behaviorIndex >= 0) {
if (event.behaviorIndex >= (int)sprite->_behaviors.size()) {
warning("Movie::resolveScriptEvent: invalid behavior index %d, ignoring", event.behaviorIndex);
} else {
scriptId = sprite->_behaviors[event.behaviorIndex].memberID;
initializerParams = sprite->_behaviors[event.behaviorIndex].initializerParams;
}
} else {
_lastClickedSpriteId = 0;
return;
}
} else {
if (!sprite->_scriptId.member) {
_lastClickedSpriteId = 0;
return;
}
scriptId = sprite->_scriptId;
}
immediate = sprite->_immediate;
}
if (_vm->getVersion() >= 600) {
event.scriptType = kScoreScript;
event.scriptId = scriptId;
if (event.behaviorIndex >= 0 && event.behaviorIndex < (int)_score->_channels[event.channelId]->_scriptInstanceList.size())
event.scriptInstance = _score->_channels[event.channelId]->_scriptInstanceList[event.behaviorIndex].u.obj;
else
warning("resolveScriptEvent: behaviorIndex %d out of range", event.behaviorIndex);
return;
}
// Sprite (score) script
ScriptContext *script = getScriptContext(kScoreScript, scriptId);
if (script) {
if (script->_eventHandlers.contains(event.event)) {
// D4-style event handler
event.scriptType = kScoreScript;
event.scriptId = scriptId;
} else if (script->_eventHandlers.contains(kEventGeneric)) {
// D3-style sprite script, not contained in a handler
// If sprite is immediate, its script is run on mouseDown, otherwise on mouseUp
if ((event.event == kEventMouseDown && immediate) || (event.event == kEventMouseUp && !immediate)) {
event.event = kEventGeneric;
event.scriptType = kScoreScript;
event.scriptId = scriptId;
}
return; // FIXME: Do not execute the cast script if there is a D3-style sprite script
}
}
}
break;
case kCastHandler:
{
// Cast script
// A strange quirk; if we're in a mouseDown event, Director will test
// at runtime to find out whatever is under the mouse and use that.
// If we're in a mouseUp event, Director will use whatever was
// discovered -at the very beginning- of the mouseDown event chain.
// This means e.g. the cast member can be swapped out from underneath in
// the mouseDown sprite script and the event passed down, which
// will mean the old cast member cast script does not get a mouseDown
// call, but it -does- get a mouseUp call.
// A bit unhinged, but we have a test that proves Director does this,
// so we have to do it too.
//
// mouseEnter and mouseLeave events should also defer to the value of channelId.
CastMemberID targetCast = _currentMouseDownCastID;
if ((event.event == kEventMouseDown) || (event.event == kEventRightMouseDown) ||
(event.event == kEventMouseEnter) || (event.event == kEventMouseLeave)) {
if (!event.channelId)
return;
Sprite *sprite = _score->getSpriteById(event.channelId);
targetCast = sprite->_castId;
}
ScriptContext *script = getScriptContext(kCastScript, targetCast);
if (script && script->_eventHandlers.contains(event.event)) {
event.scriptType = kCastScript;
event.scriptId = targetCast;
}
}
break;
case kFrameHandler:
{
/* [in D4] the enterFrame, exitFrame, idle and timeout messages
* are sent to a frame script and then a movie script. If the
* current frame has no frame script when the event occurs, the
* message goes to movie scripts.
* [p.81 of D4 docs]
*/
if (_score->_currentFrame == nullptr)
return;
if (_vm->getVersion() >= 600) {
if (_score->_scriptChannelScriptInstance.type == OBJECT) {
event.scriptType = kScoreScript;
event.scriptId = CastMemberID(); // No ID for the script channel script
event.scriptInstance = _score->_scriptChannelScriptInstance.u.obj;
}
return;
}
// Pre D6
CastMemberID scriptId = _score->_currentFrame->_mainChannels.actionId;
if (!scriptId.member)
return;
ScriptContext *script = getScriptContext(kScoreScript, scriptId);
if (!script)
return;
if (script->_eventHandlers.contains(event.event)) {
event.scriptType = kScoreScript;
event.scriptId = scriptId;
return;
}
// Scopeless statements (ie one lined lingo commands) are executed at exitFrame
// A score script can have both scopeless and scoped lingo. (eg. porting from D3.1 to D4)
// In the event of both being specified in the ScoreScript, the scopeless handler is ignored.
if (event.event == kEventExitFrame && script->_eventHandlers.contains(kEventGeneric) &&
!(script->_eventHandlers.contains(kEventExitFrame) || script->_eventHandlers.contains(kEventEnterFrame))) {
event.event = kEventGeneric;
event.scriptType = kScoreScript;
event.scriptId = scriptId;
}
}
break;
case kMovieHandler:
{
/* If more than one movie script handles the same message, Lingo
* searches the movie scripts according to their order in the cast
* window [p.81 of D4 docs]
*/
// FIXME: shared cast movie scripts could come before main movie ones
// Movie scripts are fixed, so it's fine to look them up in advance.
for (auto &cast : _casts) {
LingoArchive *archive = cast._value->_lingoArchive;
for (auto &it : archive->scriptContexts[kMovieScript]) {
if (it._value->_eventHandlers.contains(event.event)) {
event.scriptType = kMovieScript;
event.scriptId = CastMemberID(it._key, cast._key);
return;
}
}
}
LingoArchive *sharedArchive = getSharedLingoArch();
if (sharedArchive) {
for (auto &it : sharedArchive->scriptContexts[kMovieScript]) {
if (it._value->_eventHandlers.contains(event.event)) {
event.scriptType = kMovieScript;
event.scriptId = CastMemberID(it._key, DEFAULT_CAST_LIB);
return;
}
}
}
}
break;
default:
break;
}
}
void Movie::queueEvent(Common::Queue<LingoEvent> &queue, LEvent event, int targetId, Common::Point pos) {
if (_nextEventId < 0)
_nextEventId = 0;
_nextEventId++;
int eventId = _nextEventId;
int oldQueueSize = queue.size();
uint16 channelId = 0;
uint16 pointedSpriteId = 0;
// In D6+ there are multiple behavors per sprite, find the sprite
if (g_director->getVersion() >= 600) {
if (targetId == 0) {
pointedSpriteId = _score->getMouseSpriteIDFromPos(pos);
} else {
pointedSpriteId = targetId;
}
}
/* When an event occurs the message [...] is first sent to a
* primary event handler: [... if exists it is executed] and the
* event is passed on to other objects unless you explicitly stop
* the message by including the dontPassEvent command in the script
* [D4 docs page 77]
*/
/* N.B.: No primary event handlers for events other than
* keyup, keydown, mouseup, mousedown, timeout
* [see: www.columbia.edu/itc/visualarts/r4110/s2001/handouts
* /03_03_Event_Hierarchy.pdf]
*/
switch (event) {
case kEventMouseDown:
case kEventMouseUp:
case kEventRightMouseDown:
case kEventRightMouseUp:
case kEventKeyUp:
case kEventKeyDown:
case kEventTimeout:
{
// Queue a call to the the primary event handler.
// As per above, by default this will pass through to any subsequent handlers,
// unless the script calls "dontPassEvent".
queue.push(LingoEvent(event, eventId, kPrimaryHandler, true, pos));
// Key up and key down events can be sent to the channel with an active widget
if ((event == kEventKeyUp) || (event == kEventKeyDown)) {
channelId = targetId;
}
}
break;
case kEventMenuCallback:
{
CastMemberID scriptID = CastMemberID(targetId, DEFAULT_CAST_LIB);
if (getScriptContext(kEventScript, scriptID)) {
queue.push(LingoEvent(kEventGeneric, eventId, kEventScript, true, scriptID, pos));
}
}
break;
// For mouseEnter/mouseLeave events, we want to specify exactly what sprite channel to resolve to.
case kEventMouseEnter:
case kEventMouseLeave:
case kEventPrepareFrame:
case kEventBeginSprite:
case kEventEndSprite:
case kEventMouseUpOutSide: // D6+
case kEventMouseWithin: // D6+
if (targetId != 0) {
channelId = targetId;
}
break;
default:
break;
}
if (_vm->getVersion() < 400) {
// In D2-3, specific objects handle each event, with no passing
switch(event) {
case kEventMouseUp:
case kEventMouseDown:
queue.push(LingoEvent(event, eventId, kSpriteHandler, false, pos));
queue.push(LingoEvent(event, eventId, kCastHandler, false, pos));
break;
case kEventExitFrame:
queue.push(LingoEvent(event, eventId, kFrameHandler, false, pos));
break;
case kEventIdle:
case kEventStartUp:
case kEventStartMovie:
case kEventStepMovie:
case kEventStopMovie:
queue.push(LingoEvent(event, eventId, kMovieHandler, false, pos));
break;
// no-op; only handled by the primary event handler above
// empty case avoids them generating logs from the default
// unhandled event case below.
case kEventKeyUp:
case kEventKeyDown:
case kEventTimeout:
break;
default:
warning("registerEvent: Unhandled event %s", _lingo->_eventHandlerTypes[event]);
}
} else {
/* In D4+, queue any objects that responds to this event, in order of precedence.
* (Sprite -> Cast Member -> Frame -> Movie)
* Once one of these objects handles the event, any event handlers queued
* for the same event will be ignored unless the pass command was called.
*/
switch (event) {
case kEventKeyUp:
case kEventKeyDown:
case kEventMouseUp:
case kEventMouseDown:
case kEventRightMouseUp:
case kEventRightMouseDown:
case kEventBeginSprite:
case kEventEndSprite:
case kEventMouseEnter:
case kEventMouseLeave:
case kEventPrepareFrame: // D6+
case kEventMouseUpOutSide: // D6+
case kEventMouseWithin: // D6+
if (_vm->getVersion() >= 600) {
if (pointedSpriteId != 0) {
Channel *channel = _score->getChannelById(pointedSpriteId);
// Generate event for each behavior, and pass through for all but the last one.
// This is to allow multiple behaviors on a single sprite to each have a
// chance to handle the event.
for (uint i = 0; i < channel->_scriptInstanceList.size(); i++) {
bool passThrough = (i != channel->_scriptInstanceList.size() - 1);
queue.push(LingoEvent(event, eventId, kSpriteHandler, passThrough, pos, pointedSpriteId, i));
}
if (event == kEventBeginSprite || event == kEventEndSprite || event == kEventMouseUpOutSide) {
// These events do not go any further than the sprite behaviors
break;
}
} else {
// We have no sprite under the mouse, no SpriteHandler to queue.
}
} else {
queue.push(LingoEvent(event, eventId, kSpriteHandler, false, pos, channelId));
}
queue.push(LingoEvent(event, eventId, kCastHandler, false, pos, channelId));
// fall through
case kEventIdle:
case kEventEnterFrame:
case kEventExitFrame:
case kEventTimeout:
queue.push(LingoEvent(event, eventId, kFrameHandler, false, pos, channelId));
// fall through
case kEventStartUp:
case kEventStartMovie:
case kEventStepMovie:
case kEventStopMovie:
case kEventPrepareMovie: // D6
case kEventActivateWindow: // D5
case kEventDeactivateWindow: // D5
case kEventMoveWindow: // D5
case kEventResizeWindow: // D5
case kEventOpenWindow: // D5
case kEventCloseWindow: // D5
case kEventZoomWindow: // D5
queue.push(LingoEvent(event, eventId, kMovieHandler, false, pos, channelId));
break;
default:
warning("registerEvent: Unhandled event %s", _lingo->_eventHandlerTypes[event]);
}
}
if (oldQueueSize == queue.size()) {
debugC(9, kDebugEvents, "Lingo::queueEvent(%s): no event handler", _lingo->_eventHandlerTypes[event]);
}
}
void Movie::queueInputEvent(LEvent event, int targetId, Common::Point pos) {
queueEvent(_inputEventQueue, event, targetId, pos);
}
void Movie::processEvent(LEvent event, int targetId) {
Common::Queue<LingoEvent> queue;
queueEvent(queue, event, targetId);
_vm->setCurrentWindow(this->getWindow());
_lingo->processEvents(queue, false);
}
void Movie::broadcastEvent(LEvent event) {
Common::Queue<LingoEvent> queue;
for (uint i = 1; i < _score->_channels.size(); i++) {
if (_score->_channels[i] && _score->_channels[i]->_sprite && _score->_channels[i]->_sprite->_behaviors.size()) {
queueEvent(queue, event, i);
}
}
_vm->setCurrentWindow(this->getWindow());
_lingo->processEvents(queue, false);
}
void Lingo::processEvents(Common::Queue<LingoEvent> &queue, bool isInputEvent) {
if (isInputEvent && _currentInputEvent.type != VOIDSYM) {
// only one input event should be in flight at a time.
return;
}
Movie *movie = _vm->getCurrentMovie();
Score *sc = movie->getScore();
bool behavioursCompleted = false;
while (!queue.empty()) {
LingoEvent el = queue.pop();
if (sc->_playState == kPlayStopped && el.event != kEventStopMovie)
continue;
// fetch the sprite ID, script ID to call, etc if not present.
movie->resolveScriptEvent(el);
if (el.scriptType == kNoneScript) {
debugC(9, kDebugEvents, "Lingo::processEvents: no matching script for event (%s, %s, %s, %d), continuing",
_eventHandlerTypes[el.event], scriptType2str(el.scriptType), el.scriptId.asString().c_str(), el.channelId
);
continue;
}
int lastEventId = movie->_lastEventId.getValOrDefault(el.event, 0);
if (lastEventId && lastEventId == el.eventId && !_passEvent) {
debugC(5, kDebugEvents, "Lingo::processEvents: swallowed event (%s, %s, %s, %d) because _passEvent was false",
_eventHandlerTypes[el.event], scriptType2str(el.scriptType), el.scriptId.asString().c_str(), el.channelId
);
continue;
}
_passEvent = el.passByDefault;
debugC(5, kDebugEvents, "Lingo::processEvents: starting event script (%s, %s, %s, %d)",
_eventHandlerTypes[el.event], scriptType2str(el.scriptType), el.scriptId.asString().c_str(), el.channelId
);
bool completed = processEvent(el.event, el.scriptType, el.scriptId, el.channelId, el.scriptInstance);
movie->_lastEventId[el.event] = el.eventId;
if (_vm->getVersion() >= 600) {
// Reset it for further event processing
g_director->getCurrentMovie()->_currentSpriteNum = 0;
// We need to execute all behaviours before deciding if we pass
// through or not
if (el.scriptType == kScoreScript && el.passByDefault == true) {
behavioursCompleted |= completed;
completed = true;
} else {
completed |= behavioursCompleted;
}
}
if (isInputEvent && !completed) {
debugC(5, kDebugEvents, "Lingo::processEvents: context frozen on an input event, stopping");
LingoState *state = g_director->getCurrentWindow()->getLastFrozenLingoState();
if (state && !state->callstack.empty()) {
_currentInputEvent = state->callstack.front()->sp;
}
break;
}
}
}
bool Lingo::processEvent(LEvent event, ScriptType st, CastMemberID scriptId, int channelId, AbstractObject *obj) {
_currentChannelId = channelId;
if (!_eventHandlerTypes.contains(event))
error("processEvent: Unknown event %d", event);
if (g_director->getVersion() >= 600 && st == kScoreScript && obj) {
if (obj->getMethod(_eventHandlerTypes[event]).type != VOIDSYM) {
g_director->getCurrentMovie()->_currentSpriteNum = channelId;
push(Datum(obj));
LC::call(_eventHandlerTypes[event], 1, false);
return execute();
} else {
return true;
}
}
ScriptContext *script = g_director->getCurrentMovie()->getScriptContext(st, scriptId);
int nargs = 0;
if (script && script->_eventHandlers.contains(event)) {
debugC(1, kDebugEvents, "Lingo::processEvent(%s, %s, %s): executing event handler", _eventHandlerTypes[event], scriptType2str(st), scriptId.asString().c_str());
g_debugger->eventHook(event);
// Normally event handlers are called with no arguments, however RolloverToolkit expects
// the first argument to be the sprite number.
if ((event == kEventMouseEnter && script->_eventHandlers[event].name->equalsIgnoreCase("startRollover")) ||
(event == kEventMouseLeave && script->_eventHandlers[event].name->equalsIgnoreCase("endRollover"))) {
push(Datum(channelId));
nargs = 1;
}
LC::call(script->_eventHandlers[event], nargs, false);
return execute();
} else {
debugC(9, kDebugEvents, "Lingo::processEvent(%s, %s, %s): no handler", _eventHandlerTypes[event], scriptType2str(st), scriptId.asString().c_str());
}
return true;
}
/***********************
* Script Instances
***********************/
void Score::killScriptInstances(int frameNum) {
if (_version < kFileVer600) // No-op for early Directors
return;
if (frameNum < _currentFrame->_mainChannels.scriptSpriteInfo.startFrame ||
frameNum > _currentFrame->_mainChannels.scriptSpriteInfo.endFrame) {
if (_scriptChannelScriptInstance.type == OBJECT) {
_scriptChannelScriptInstance = Datum();
debugC(1, kDebugLingoExec, "Score::killScriptInstances(): Killed script instances for script channel. frame %d [%d-%d]",
frameNum,
_currentFrame->_mainChannels.scriptSpriteInfo.startFrame,
_currentFrame->_mainChannels.scriptSpriteInfo.endFrame);
}
}
for (int i = 0; i < (int)_channels.size(); i++) {
Channel *channel = _channels[i];
if (channel->_scriptInstanceList.size() == 0)
continue;
if (frameNum < channel->_startFrame || frameNum > channel->_endFrame) {
bool prevDis = _disableGoPlayUpdateStage;
_disableGoPlayUpdateStage = true;
_movie->processEvent(kEventEndSprite, i);
_disableGoPlayUpdateStage = prevDis;
channel->_scriptInstanceList.clear();
channel->_sprite->_behaviors.clear();
debugC(1, kDebugLingoExec, "Score::killScriptInstances(): Killed script instances for channel %d. frame %d [%d-%d]",
i + 1, frameNum, channel->_startFrame, channel->_endFrame);
channel->_startFrame = channel->_endFrame = -1;
}
}
}
Datum Score::createScriptInstance(BehaviorElement *behavior) {
// Instantiate the behavior
ScriptContext *scr = _movie->getScriptContext(kScoreScript, behavior->memberID);
// Some movies have behaviors with missing scripts
if (scr == nullptr) {
debugC(7, kDebugLingoExec, "Score::createScriptInstance(): Missing script for behavior %s", behavior->toString().c_str());
return Datum();
}
g_lingo->push(scr);
LC::call("new", 1, true);
Datum instance = g_lingo->pop();
if (instance.type != OBJECT) {
warning("Score::createScriptInstance(): Could not instantiate behavior %s", behavior->toString().c_str());
return Datum();
}
debugC(1, kDebugLingoExec, " Instantiated behavior %s", behavior->toString().c_str());
// No initializer, we are done
if (behavior->initializerIndex == 0)
return instance;
// Evaluate the params
g_lingo->push(behavior->initializerParams);
LB::b_value(1);
g_lingo->execute();
if (debugChannelSet(5, kDebugLingoExec)) {
g_lingo->printStack(" Parsed behavior parameters: ", 0);
}
if (g_lingo->_state->stack.size() == 0) {
warning("Score::createScriptInstance(): Could not evaluate initializer params '%s' for behavior %s",
behavior->initializerParams.c_str(), behavior->toString().c_str());
return instance;
}
Datum proplist = _lingo->pop();
if (proplist.type != PARRAY) {
warning("Score::createScriptInstance(): Could not evaluate initializer params '%s' for behavior %s",
behavior->initializerParams.c_str(), behavior->toString().c_str());
return instance;
}
debugC(2, kDebugLingoExec, " Setting %d properties", proplist.u.parr->arr.size());
for (uint k = 0; k < proplist.u.parr->arr.size(); k++) {
Datum key = proplist.u.parr->arr[k].p;
Datum val = proplist.u.parr->arr[k].v;
instance.u.obj->setProp(key.asString(), val);
}
return instance;
}
void Score::createScriptInstances(int frameNum) {
if (_version < kFileVer600) // No-op for early Directors
return;
if (frameNum >= _currentFrame->_mainChannels.scriptSpriteInfo.startFrame &&
frameNum <= _currentFrame->_mainChannels.scriptSpriteInfo.endFrame) {
// We have no instantiated script
if (_scriptChannelScriptInstance.type != OBJECT) {
if (_currentFrame->_mainChannels.behaviors.size() > 0) {
debugC(1, kDebugLingoExec, "Score::createScriptInstances(): Creating script instances for script channel, frames [%d-%d]",
_currentFrame->_mainChannels.scriptSpriteInfo.startFrame,
_currentFrame->_mainChannels.scriptSpriteInfo.endFrame);
_scriptChannelScriptInstance = createScriptInstance(&_currentFrame->_mainChannels.behaviors[0]);
}
}
}
for (int i = 0; i < (int)_channels.size(); i++) {
Channel *channel = _channels[i];
Sprite *sprite = channel->_sprite;
// The frame does not belong to the range
if (frameNum < channel->_startFrame || frameNum > channel->_endFrame)
continue;
// We create scriptInstance only for new sprites
if (channel->_scriptInstanceList.size() != 0)
continue;
// No behaviors, nothing to do
if (sprite->_behaviors.size() == 0)
continue;
debugC(1, kDebugLingoExec, "Score::createScriptInstances(): Creating script instances for channel %d, %d behaviors, frames [%d-%d]",
i + 1, sprite->_behaviors.size(), channel->_startFrame, channel->_endFrame);
for (uint j = 0; j < sprite->_behaviors.size(); j++) {
Datum instance = createScriptInstance(&sprite->_behaviors[j]);
if (instance.type != OBJECT) {
if (!instance.isVoid())
warning("Score::createScriptInstances(): Could not instantiate behavior %s", sprite->_behaviors[j].toString().c_str());
continue;
}
channel->_scriptInstanceList.push_back(instance);
}
bool prevDis = _disableGoPlayUpdateStage;
_disableGoPlayUpdateStage = true;
_movie->processEvent(kEventBeginSprite, i);
_disableGoPlayUpdateStage = prevDis;
}
}
} // End of namespace Director

View File

@@ -0,0 +1,275 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "audio/audiostream.h"
#include "common/file.h"
#include "common/macresman.h"
#include "common/system.h"
#include "graphics/macgui/macwindowmanager.h"
#include "director/director.h"
#include "director/archive.h"
#include "director/cursor.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/sound.h"
#include "director/window.h"
#include "director/lingo/lingo-builtins.h"
namespace Director {
void Lingo::func_goto(Datum &frame, Datum &movie, bool calledfromgo) {
_vm->_playbackPaused = false;
if (!_vm->getCurrentMovie())
return;
if (movie.type == VOID && frame.type == VOID)
return;
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
if (score->_disableGoPlayUpdateStage) {
warning("Lingo::func_goto(): ignoring goto due to disableGoPlayUpdateStage flag");
return;
}
stage->_skipFrameAdvance = true;
// If there isn't already frozen Lingo (e.g. from a previous func_goto we haven't yet unfrozen),
// freeze this script context. We'll return to it after entering the next frame.
// Returning from a script with "play done" does not freeze the state. Instead it obliterates it.
if (!g_lingo->_playDone)
g_lingo->_freezeState = true;
if (movie.type != VOID) {
Common::String movieFilenameRaw = movie.asString();
if (!stage->setNextMovie(movieFilenameRaw))
return;
// If we reached here from b_go, and the movie is getting swapped out,
// reset all of the custom event handlers.
if (calledfromgo)
g_lingo->resetLingoGo();
if (g_lingo->_updateMovieEnabled) {
// Save the movie when branching to another movie.
LB::b_saveMovie(0);
}
score->_playState = kPlayStopped;
stage->_nextMovie.frameS.clear();
stage->_nextMovie.frameI = -1;
if (frame.type == STRING) {
debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to movie \"%s\", frame \"%s\"", movieFilenameRaw.c_str(), frame.u.s->c_str());
stage->_nextMovie.frameS = *frame.u.s;
} else if (frame.type != VOID) {
debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to movie \"%s\", frame %d", movieFilenameRaw.c_str(), frame.asInt());
stage->_nextMovie.frameI = frame.asInt();
} else {
debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to start of movie \"%s\"", movieFilenameRaw.c_str());
}
// Set cursor to watch.
score->_defaultCursor.readFromResource(4);
score->renderCursor(stage->getMousePos());
return;
}
if (frame.type == STRING) {
debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to frame \"%s\"", frame.u.s->c_str());
score->setStartToLabel(*frame.u.s);
} else {
debugC(3, kDebugLingoExec, "Lingo::func_goto(): going to frame %d", frame.asInt());
score->setCurrentFrame(frame.asInt());
}
// Since the frames are not going to be consecutive, we might run into
// an endge case, so better kill behaviors proactively.
score->killScriptInstances(score->getNextFrame());
}
void Lingo::func_gotoloop() {
if (!_vm->getCurrentMovie())
return;
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
debugC(3, kDebugLingoExec, "Lingo::func_gotoloop(): looping frame %d", score->getCurrentFrameNum());
score->gotoLoop();
stage->_skipFrameAdvance = true;
}
void Lingo::func_gotonext() {
if (!_vm->getCurrentMovie())
return;
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
score->gotoNext();
debugC(3, kDebugLingoExec, "Lingo::func_gotonext(): going to next frame %d", score->getNextFrame());
stage->_skipFrameAdvance = true;
}
void Lingo::func_gotoprevious() {
if (!_vm->getCurrentMovie())
return;
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
score->gotoPrevious();
debugC(3, kDebugLingoExec, "Lingo::func_gotoprevious(): going to previous frame %d", score->getNextFrame());
stage->_skipFrameAdvance = true;
}
void Lingo::func_play(Datum &frame, Datum &movie) {
MovieReference ref;
Window *stage = _vm->getCurrentWindow();
if (stage->getCurrentMovie()->getScore()->_disableGoPlayUpdateStage) {
warning("Lingo::func_play(): ignoring play due to disableGoPlayUpdateStage flag");
return;
}
// play #done
if (frame.type == SYMBOL) {
if (!frame.u.s->equals("done")) {
warning("Lingo::func_play: unknown symbol: #%s", frame.u.s->c_str());
return;
}
_playDone = true;
if (stage->_movieStack.empty()) { // No op if no nested movies
return;
}
ref = stage->_movieStack.back();
stage->_movieStack.pop_back();
Datum m, f;
if (ref.movie.empty()) {
m.type = VOID;
} else {
m.type = STRING;
m.u.s = new Common::String(ref.movie);
}
f.type = INT;
f.u.i = ref.frameI;
func_goto(f, m);
return;
}
if (!_vm->getCurrentMovie()) {
warning("Lingo::func_play(): no movie");
return;
}
if (movie.type != VOID) {
ref.movie = _vm->getCurrentMovie()->_movieArchive->getPathName().toString(g_director->_dirSeparator);
}
ref.frameI = _vm->getCurrentMovie()->getScore()->getCurrentFrameNum();
// if we are issuing play command from script channel script. then play done should return to next frame
if (g_lingo->_currentChannelId == 0)
ref.frameI++;
stage->_movieStack.push_back(ref);
func_goto(frame, movie);
_freezePlay = true;
}
void Lingo::func_cursor(Datum cursorDatum) {
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
if (cursorDatum.type == ARRAY){
score->_defaultCursor.readFromCast(cursorDatum);
} else {
score->_defaultCursor.readFromResource(cursorDatum);
}
score->_cursorDirty = true;
}
void Lingo::func_beep(int repeats) {
for (int r = 1; r <= repeats; r++) {
_vm->getCurrentWindow()->getSoundManager()->systemBeep();
if (r < repeats)
g_director->delayMillis(400);
}
}
int Lingo::func_marker(int m) {
if (!_vm->getCurrentMovie())
return 0;
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
int labelNumber = score->getCurrentLabelNumber();
if (m != 0) {
if (m < 0) {
for (int marker = 0; marker > m; marker--)
labelNumber = score->getPreviousLabelNumber(labelNumber);
} else {
for (int marker = 0; marker < m; marker++)
labelNumber = score->getNextLabelNumber(labelNumber);
}
}
return labelNumber;
}
uint16 Lingo::func_label(Datum &label) {
Window *stage = _vm->getCurrentWindow();
Score *score = stage->getCurrentMovie()->getScore();
if (!score->_labels)
return 0;
if (label.type == STRING)
return score->getLabel(*label.u.s);
int num = CLIP<int>(label.asInt() - 1, 0, score->_labels->size() - 1);
uint16 res = score->getNextLabelNumber(0);
while (--num > 0)
res = score->getNextLabelNumber(res);
return res;
}
} // End of namespace Director

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_YY_ENGINES_DIRECTOR_LINGO_LINGO_GR_H_INCLUDED
# define YY_YY_ENGINES_DIRECTOR_LINGO_LINGO_GR_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 1
#endif
#if YYDEBUG
extern int yydebug;
#endif
/* Token kinds. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
YYEMPTY = -2,
YYEOF = 0, /* "end of file" */
YYerror = 256, /* error */
YYUNDEF = 257, /* "invalid token" */
tUNARY = 258, /* tUNARY */
tINT = 259, /* tINT */
tFLOAT = 260, /* tFLOAT */
tVARID = 261, /* tVARID */
tSTRING = 262, /* tSTRING */
tSYMBOL = 263, /* tSYMBOL */
tENDCLAUSE = 264, /* tENDCLAUSE */
tCAST = 265, /* tCAST */
tFIELD = 266, /* tFIELD */
tSCRIPT = 267, /* tSCRIPT */
tWINDOW = 268, /* tWINDOW */
tMEMBER = 269, /* tMEMBER */
tCASTLIB = 270, /* tCASTLIB */
tDELETE = 271, /* tDELETE */
tDOWN = 272, /* tDOWN */
tELSE = 273, /* tELSE */
tEXIT = 274, /* tEXIT */
tFRAME = 275, /* tFRAME */
tGLOBAL = 276, /* tGLOBAL */
tGO = 277, /* tGO */
tHILITE = 278, /* tHILITE */
tIF = 279, /* tIF */
tIN = 280, /* tIN */
tINTO = 281, /* tINTO */
tMACRO = 282, /* tMACRO */
tRETURN = 283, /* tRETURN */
tMOVIE = 284, /* tMOVIE */
tNEXT = 285, /* tNEXT */
tOF = 286, /* tOF */
tPREVIOUS = 287, /* tPREVIOUS */
tPUT = 288, /* tPUT */
tREPEAT = 289, /* tREPEAT */
tSET = 290, /* tSET */
tTHEN = 291, /* tTHEN */
tTO = 292, /* tTO */
tWHEN = 293, /* tWHEN */
tWITH = 294, /* tWITH */
tWHILE = 295, /* tWHILE */
tFACTORY = 296, /* tFACTORY */
tOPEN = 297, /* tOPEN */
tPLAY = 298, /* tPLAY */
tINSTANCE = 299, /* tINSTANCE */
tGE = 300, /* tGE */
tLE = 301, /* tLE */
tEQ = 302, /* tEQ */
tNEQ = 303, /* tNEQ */
tAND = 304, /* tAND */
tOR = 305, /* tOR */
tNOT = 306, /* tNOT */
tMOD = 307, /* tMOD */
tAFTER = 308, /* tAFTER */
tBEFORE = 309, /* tBEFORE */
tCONCAT = 310, /* tCONCAT */
tCONTAINS = 311, /* tCONTAINS */
tSTARTS = 312, /* tSTARTS */
tCHAR = 313, /* tCHAR */
tCHARS = 314, /* tCHARS */
tITEM = 315, /* tITEM */
tITEMS = 316, /* tITEMS */
tLINE = 317, /* tLINE */
tLINES = 318, /* tLINES */
tWORD = 319, /* tWORD */
tWORDS = 320, /* tWORDS */
tABBREVIATED = 321, /* tABBREVIATED */
tABBREV = 322, /* tABBREV */
tABBR = 323, /* tABBR */
tLONG = 324, /* tLONG */
tSHORT = 325, /* tSHORT */
tDATE = 326, /* tDATE */
tLAST = 327, /* tLAST */
tMENU = 328, /* tMENU */
tMENUS = 329, /* tMENUS */
tMENUITEM = 330, /* tMENUITEM */
tMENUITEMS = 331, /* tMENUITEMS */
tNUMBER = 332, /* tNUMBER */
tTHE = 333, /* tTHE */
tTIME = 334, /* tTIME */
tXTRAS = 335, /* tXTRAS */
tCASTLIBS = 336, /* tCASTLIBS */
tSOUND = 337, /* tSOUND */
tSPRITE = 338, /* tSPRITE */
tINTERSECTS = 339, /* tINTERSECTS */
tWITHIN = 340, /* tWITHIN */
tTELL = 341, /* tTELL */
tPROPERTY = 342, /* tPROPERTY */
tON = 343, /* tON */
tMETHOD = 344, /* tMETHOD */
tENDIF = 345, /* tENDIF */
tENDREPEAT = 346, /* tENDREPEAT */
tENDTELL = 347, /* tENDTELL */
tASSERTERROR = 348 /* tASSERTERROR */
};
typedef enum yytokentype yytoken_kind_t;
#endif
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
union YYSTYPE
{
#line 118 "engines/director/lingo/lingo-gr.y"
Common::String *s;
int i;
double f;
Director::ChunkType chunktype;
struct {
Common::String *eventName;
Common::String *stmt;
} w;
Director::IDList *idlist;
Director::Node *node;
Director::NodeList *nodelist;
#line 172 "engines/director/lingo/lingo-gr.h"
};
typedef union YYSTYPE YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval;
int yyparse (void);
#endif /* !YY_YY_ENGINES_DIRECTOR_LINGO_LINGO_GR_H_INCLUDED */

View File

@@ -0,0 +1,926 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Heavily inspired by hoc
// Copyright (C) AT&T 1995
// All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and
// its documentation for any purpose and without fee is hereby
// granted, provided that the above copyright notice appear in all
// copies and that both that the copyright notice and this
// permission notice and warranty disclaimer appear in supporting
// documentation, and that the name of AT&T or any of its entities
// not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
// THIS SOFTWARE.
%require "3.6"
%defines "engines/director/lingo/lingo-gr.h"
%output "engines/director/lingo/lingo-gr.cpp"
%define parse.error custom
%define parse.trace
// %glr-parser
%{
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
#define FORBIDDEN_SYMBOL_EXCEPTION_fread
#define FORBIDDEN_SYMBOL_EXCEPTION_stdin
#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
#define FORBIDDEN_SYMBOL_EXCEPTION_getc
#include "common/endian.h"
#include "common/hash-str.h"
#include "common/rect.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-ast.h"
#include "director/lingo/lingo-code.h"
#include "director/lingo/lingo-codegen.h"
#include "director/lingo/lingo-gr.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/lingo-the.h"
extern int yylex();
using namespace Director;
static void yyerror(const char *s) {
LingoCompiler *compiler = g_lingo->_compiler;
compiler->_hadError = true;
warning("%s LINGO: %s at line %d col %d in %s id: %d",
(g_director->_noFatalLingoError ? "####" : "######################"), s, compiler->_linenumber, compiler->_colnumber, scriptType2str(compiler->_assemblyContext->_scriptType),
compiler->_assemblyContext->_id);
if (compiler->_lines[2] != compiler->_lines[1])
warning("# %3d: %s", compiler->_linenumber - 2, Common::String(compiler->_lines[2], compiler->_lines[1] - 1).c_str());
if (compiler->_lines[1] != compiler->_lines[0])
warning("# %3d: %s", compiler->_linenumber - 1, Common::String(compiler->_lines[1], compiler->_lines[0] - 1).c_str());
const char *ptr = compiler->_lines[0];
while (*ptr && *ptr != '\n')
ptr++;
warning("# %3d: %s", compiler->_linenumber, Common::String(compiler->_lines[0], ptr).c_str());
Common::String arrow;
for (uint i = 0; i < compiler->_colnumber; i++)
arrow += ' ';
warning("# %s^ about here", arrow.c_str());
}
static void checkEnd(Common::String *token, Common::String *expect, bool required) {
if (required) {
if (token->compareToIgnoreCase(*expect)) {
Common::String err = Common::String::format("end mismatch. Expected %s but got %s", expect->c_str(), token->c_str());
yyerror(err.c_str());
}
}
}
%}
%union {
Common::String *s;
int i;
double f;
Director::ChunkType chunktype;
struct {
Common::String *eventName;
Common::String *stmt;
} w;
Director::IDList *idlist;
Director::Node *node;
Director::NodeList *nodelist;
}
%token tUNARY
%token<i> tINT
%token<f> tFLOAT
%token<s> tVARID tSTRING tSYMBOL
%token<s> tENDCLAUSE
%token tCAST tFIELD tSCRIPT tWINDOW tMEMBER tCASTLIB
%token tDELETE tDOWN tELSE tEXIT tFRAME tGLOBAL tGO tHILITE tIF tIN tINTO tMACRO tRETURN
%token tMOVIE tNEXT tOF tPREVIOUS tPUT tREPEAT tSET tTHEN tTO tWHEN
%token tWITH tWHILE tFACTORY tOPEN tPLAY tINSTANCE
%token tGE tLE tEQ tNEQ tAND tOR tNOT tMOD
%token tAFTER tBEFORE tCONCAT tCONTAINS tSTARTS
%token tCHAR tCHARS tITEM tITEMS tLINE tLINES tWORD tWORDS
%token tABBREVIATED tABBREV tABBR tLONG tSHORT
%token tDATE tLAST tMENU tMENUS tMENUITEM tMENUITEMS tNUMBER tTHE tTIME tXTRAS tCASTLIBS
%token tSOUND tSPRITE tINTERSECTS tWITHIN tTELL tPROPERTY
%token tON tMETHOD tENDIF tENDREPEAT tENDTELL
%token tASSERTERROR
// D5
// %token tCASE tOTHERWISE
%type<w> tWHEN
// TOP-LEVEL STUFF
%type<node> script scriptpart
%type<nodelist> scriptpartlist
// MACRO
%type<node> macro
// FACTORY
%type<node> factory method
%type<nodelist> methodlist nonemptymethodlist
%type<node> methodlistline
// HANDLER
%type<node> handler
// GENERIC VAR STUFF
%type<s> CMDID ID
%type<idlist> idlist nonemptyidlist
// STATEMENT
%type<node> stmt stmt_insideif stmtoneliner
%type<node> proc asgn definevars
%type<node> ifstmt ifelsestmt loop tell when
%type<nodelist> cmdargs frameargs stmtlist nonemptystmtlist stmtlist_insideif nonemptystmtlist_insideif
%type<node> stmtlistline stmtlistline_insideif
// EXPRESSION
%type<node> simpleexpr_nounarymath simpleexpr
%type<node> unarymath
%type<node> expr expr_nounarymath expr_noeq sprite
%type<node> var varorchunk varorthe
%type<chunktype> chunktype
%type<node> the theobj menu thedatetime thenumberof
%type<node> writablethe writabletheobj
%type<node> list proppair
%type<node> chunk object
%type<nodelist> refargs proplist exprlist nonemptyexprlist
%left tAND tOR
%left '<' tLE '>' tGE tEQ tNEQ tCONTAINS tSTARTS
%left '&' tCONCAT
%left '+' '-'
%left '*' '/' tMOD
%right tUNARY
// %right tCAST tFIELD tSCRIPT tWINDOW
// %nonassoc tVARID
%destructor { delete $$; } <s>
%destructor { delete $$; } <node>
%%
// TOP-LEVEL STUFF
script: scriptpartlist { g_lingo->_compiler->_assemblyAST = Common::SharedPtr<Node>(new ScriptNode($scriptpartlist)); $$ = nullptr; } ;
scriptpartlist: scriptpart[item] {
NodeList *list = new NodeList;
if ($item) {
list->push_back($item);
}
$$ = list; }
| scriptpartlist[prev] scriptpart[item] {
if ($item) {
$prev->push_back($item);
}
$$ = $prev; }
;
scriptpart: '\n' { $$ = nullptr; }
| macro
| factory
| handler
| stmt
| tENDCLAUSE endargdef '\n' { $$ = nullptr; delete $tENDCLAUSE; } // stray `end`s are allowed for some reason
;
// MACRO
// Special Note The macro keyword is retained in Director 3.0 to maintain compatibility
// with scripts developed under Version 2.0. When writing new scripts, or editing old
// scripts, you should use handlers instead of macros. (Handlers are defined with the on keyword.)
//
// Syntax:
//
// -- [comment]
// macro macroName [argument1] [, argument2]
// [, argument3]
// [statement1]
// [statement2]
//
// Keyword. Defines a macro. A macro is a multiple-line script defined
// in the Text window. Macros can accept arguments (inputs) and
// optionally return a result. Macros can call other macros and can be
// called from any other script or factory.
//
// The first line of a castmember in the Text window that contains a macro must be
// a comment (--). You can define more than one macro in a given text castmember.
// The macro definition ends where the next macro (or factory) begins.
//
// See also:
// on keyword
macro: tMACRO ID idlist '\n' stmtlist { $$ = new HandlerNode($ID, $idlist, $stmtlist); } ;
// FACTORY
factory: tFACTORY ID '\n' methodlist { $$ = new FactoryNode($ID, $methodlist); } ;
method: tMETHOD ID idlist '\n' stmtlist { $$ = new HandlerNode($ID, $idlist, $stmtlist); } ;
methodlist: /* empty */ { $$ = new NodeList; }
| nonemptymethodlist
;
nonemptymethodlist: methodlistline[item] {
NodeList *list = new NodeList;
if ($item) {
list->push_back($item);
}
$$ = list; }
| nonemptymethodlist[prev] methodlistline[item] {
if ($item) {
$prev->push_back($item);
}
$$ = $prev; }
;
methodlistline: '\n' { $$ = nullptr; }
| method
| tENDCLAUSE endargdef '\n' { $$ = nullptr; delete $tENDCLAUSE; } // stray `end`s are allowed for some reason
;
// HANDLER
handler: tON ID idlist '\n' stmtlist tENDCLAUSE endargdef '\n' { // D3
$$ = new HandlerNode($ID, $idlist, $stmtlist);
checkEnd($tENDCLAUSE, $ID, false);
delete $tENDCLAUSE; }
| tON ID idlist '\n' stmtlist { // D4. No 'end' clause
$$ = new HandlerNode($ID, $idlist, $stmtlist); }
;
endargdef: /* nothing */
| ID { delete $ID; }
| endargdef ',' ID { delete $ID; }
;
// GENERIC VAR STUFF
// This is only the identifiers that can appaear at the start of a line
// and will not conflict with other statement types.
CMDID: tVARID
| tABBREVIATED { $$ = new Common::String("abbreviated"); }
| tABBREV { $$ = new Common::String("abbrev"); }
| tABBR { $$ = new Common::String("abbr"); }
| tAFTER { $$ = new Common::String("after"); }
| tBEFORE { $$ = new Common::String("before"); }
| tCAST { $$ = new Common::String("cast"); }
| tCASTLIB { $$ = new Common::String("castLib"); }
| tCHAR { $$ = new Common::String("char"); }
| tCHARS { $$ = new Common::String("chars"); }
| tDATE { $$ = new Common::String("date"); }
| tDELETE { $$ = new Common::String("delete"); }
| tDOWN { $$ = new Common::String("down"); }
| tFIELD { $$ = new Common::String("field"); }
| tFRAME { $$ = new Common::String("frame"); }
| tHILITE { $$ = new Common::String("hilite"); }
| tIN { $$ = new Common::String("in"); }
| tINTERSECTS { $$ = new Common::String("intersects"); }
| tINTO { $$ = new Common::String("into"); }
| tITEM { $$ = new Common::String("item"); }
| tITEMS { $$ = new Common::String("items"); }
| tLAST { $$ = new Common::String("last"); }
| tLINE { $$ = new Common::String("line"); }
| tLINES { $$ = new Common::String("lines"); }
| tLONG { $$ = new Common::String("long"); }
| tMEMBER { $$ = new Common::String("member"); }
| tMENU { $$ = new Common::String("menu"); }
| tMENUITEM { $$ = new Common::String("menuItem"); }
| tMENUITEMS { $$ = new Common::String("menuItems"); }
| tMOVIE { $$ = new Common::String("movie"); }
| tNEXT { $$ = new Common::String("next"); }
| tNUMBER { $$ = new Common::String("number"); }
| tOF { $$ = new Common::String("of"); }
| tPREVIOUS { $$ = new Common::String("previous"); }
| tREPEAT { $$ = new Common::String("repeat"); }
| tSCRIPT { $$ = new Common::String("script"); }
| tASSERTERROR { $$ = new Common::String("scummvmAssertError"); }
| tSHORT { $$ = new Common::String("short"); }
| tSOUND { $$ = new Common::String("sound"); }
| tSPRITE { $$ = new Common::String("sprite"); }
| tTHE { $$ = new Common::String("the"); }
| tTIME { $$ = new Common::String("time"); }
| tTO { $$ = new Common::String("to"); }
| tWHILE { $$ = new Common::String("while"); }
| tWINDOW { $$ = new Common::String("window"); }
| tWITH { $$ = new Common::String("with"); }
| tWITHIN { $$ = new Common::String("within"); }
| tWORD { $$ = new Common::String("word"); }
| tWORDS { $$ = new Common::String("words"); }
;
ID: CMDID
| tELSE { $$ = new Common::String("else"); }
| tENDCLAUSE { $$ = new Common::String("end"); delete $tENDCLAUSE; }
| tEXIT { $$ = new Common::String("exit"); }
| tFACTORY { $$ = new Common::String("factory"); }
| tGLOBAL { $$ = new Common::String("global"); }
| tGO { $$ = new Common::String("go"); }
| tIF { $$ = new Common::String("if"); }
| tINSTANCE { $$ = new Common::String("instance"); }
| tMACRO { $$ = new Common::String("macro"); }
| tMETHOD { $$ = new Common::String("method"); }
| tON { $$ = new Common::String("on"); }
| tOPEN { $$ = new Common::String("open"); }
| tPLAY { $$ = new Common::String("play"); }
| tPROPERTY { $$ = new Common::String("property"); }
| tPUT { $$ = new Common::String("put"); }
| tRETURN { $$ = new Common::String("return"); }
| tSET { $$ = new Common::String("set"); }
| tTELL { $$ = new Common::String("tell"); }
| tTHEN { $$ = new Common::String("then"); }
;
idlist: /* empty */ { $$ = new IDList; }
| nonemptyidlist
| nonemptyidlist ',' // allow trailing comma
;
nonemptyidlist: ID[item] {
Common::Array<Common::String *> *list = new IDList;
list->push_back($item);
$$ = list; }
| nonemptyidlist[prev] ',' ID[item] {
$prev->push_back($item);
$$ = $prev; }
;
// STATEMENT
// N.B. A statement must always be terminated by a '\n' symbol.
// Sometimes this '\n' is in a nested statement (e.g. tIF expr tTHEN stmt).
// It may not look like there's a '\n', but it's there.
stmt: stmt_insideif
| tENDIF '\n' { $$ = nullptr; } // stray `end if`s are allowed for some reason
;
stmt_insideif: stmtoneliner
| ifstmt
| ifelsestmt
| loop
| tell
| when
;
stmtoneliner: proc
| asgn
| definevars
;
proc: CMDID cmdargs '\n' { $$ = new CmdNode($CMDID, $cmdargs, g_lingo->_compiler->_linenumber - 1); }
| tPUT cmdargs '\n' { $$ = new CmdNode(new Common::String("put"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
| tGO cmdargs '\n' { $$ = new CmdNode(new Common::String("go"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
| tGO frameargs '\n' { $$ = new CmdNode(new Common::String("go"), $frameargs, g_lingo->_compiler->_linenumber - 1); }
| tPLAY cmdargs '\n' { $$ = new CmdNode(new Common::String("play"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
| tPLAY frameargs '\n' { $$ = new CmdNode(new Common::String("play"), $frameargs, g_lingo->_compiler->_linenumber - 1); }
| tOPEN cmdargs '\n' { $$ = new CmdNode(new Common::String("open"), $cmdargs, g_lingo->_compiler->_linenumber - 1); }
| tOPEN expr[arg1] tWITH expr[arg2] '\n' {
NodeList *args = new NodeList;
args->push_back($arg1);
args->push_back($arg2);
$$ = new CmdNode(new Common::String("open"), args, g_lingo->_compiler->_linenumber - 1); }
| tNEXT tREPEAT '\n' { $$ = new NextRepeatNode(); }
| tEXIT tREPEAT '\n' { $$ = new ExitRepeatNode(); }
| tEXIT '\n' { $$ = new ExitNode(); }
| tRETURN '\n' { $$ = new ReturnNode(nullptr); }
| tRETURN expr '\n' { $$ = new ReturnNode($expr); }
| tDELETE chunk '\n' { $$ = new DeleteNode($chunk); }
| tHILITE chunk '\n' { $$ = new HiliteNode($chunk); }
| tASSERTERROR stmtoneliner { $$ = new AssertErrorNode($stmtoneliner); }
;
cmdargs: /* empty */ {
// This matches `cmd`
$$ = new NodeList; }
| expr trailingcomma {
// This matches `cmd arg` and `cmd(arg)`
NodeList *args = new NodeList;
args->push_back($expr);
$$ = args; }
| expr ',' nonemptyexprlist[args] trailingcomma {
// This matches `cmd arg, ...)
$args->insert_at(0, $expr);
$$ = $args; }
| expr expr_nounarymath trailingcomma {
// This matches `cmd arg arg`
NodeList *args = new NodeList;
args->push_back($expr);
args->push_back($expr_nounarymath);
$$ = args; }
| expr expr_nounarymath ',' nonemptyexprlist[args] trailingcomma {
// This matches `cmd arg arg, ...`
$args->insert_at(0, $expr_nounarymath);
$args->insert_at(0, $expr);
$$ = $args; }
| '(' ')' {
// This matches `cmd()`
$$ = new NodeList; }
| '(' expr ',' ')' {
// This matches `cmd(arg,)`
NodeList *args = new NodeList;
args->push_back($expr);
$$ = args; }
| '(' expr ',' nonemptyexprlist[args] trailingcomma ')' {
// This matches `cmd(arg, ...)`
$args->insert_at(0, $expr);
$$ = $args; }
| '(' var[method] expr_nounarymath trailingcomma ')' {
// This matches `obj(method arg)`
NodeList *args = new NodeList;
args->push_back($method);
args->push_back($expr_nounarymath);
$$ = args; }
| '(' var[method] expr_nounarymath ',' nonemptyexprlist[args] trailingcomma ')' {
// This matches `obj(method arg, ...)`
$args->insert_at(0, $expr_nounarymath);
$args->insert_at(0, $method);
$$ = $args; }
;
trailingcomma: /* empty */ | ',' ;
frameargs:
// On the off chance that we encounter something like `play frame done`
// we will wrap the frame arg in a FrameNode. This has no purpose other than
// to avoid detecting this case as `play done`.
tFRAME expr[frame] {
// This matches `play frame arg`
NodeList *args = new NodeList;
args->push_back(new FrameNode($frame));
$$ = args; }
| tMOVIE expr[movie] {
// This matches `play movie arg`
NodeList *args = new NodeList;
args->push_back(new IntNode(1));
args->push_back(new MovieNode($movie));
$$ = args; }
| tFRAME expr[frame] tOF tMOVIE expr[movie] {
// This matches `play frame arg of movie arg`
NodeList *args = new NodeList;
args->push_back(new FrameNode($frame));
args->push_back(new MovieNode($movie));
$$ = args; }
| expr[frame] tOF tMOVIE expr[movie] {
// This matches `play arg of movie arg` (weird but valid)
NodeList *args = new NodeList;
args->push_back($frame);
args->push_back(new MovieNode($movie));
$$ = args; }
| tFRAME expr[frame] expr_nounarymath[movie] {
// This matches `play frame arg arg` (also weird but valid)
NodeList *args = new NodeList;
args->push_back(new FrameNode($frame));
args->push_back($movie);
$$ = args; }
;
asgn: tPUT expr tINTO varorchunk '\n' { $$ = new PutIntoNode($expr, $varorchunk); }
| tPUT expr tAFTER varorchunk '\n' { $$ = new PutAfterNode($expr, $varorchunk); }
| tPUT expr tBEFORE varorchunk '\n' { $$ = new PutBeforeNode($expr, $varorchunk); }
| tSET varorthe to expr '\n' { $$ = new SetNode($varorthe, $expr); }
;
to: tTO | tEQ ;
definevars: tGLOBAL idlist '\n' { $$ = new GlobalNode($idlist); }
| tPROPERTY idlist '\n' { $$ = new PropertyNode($idlist); }
| tINSTANCE idlist '\n' { $$ = new InstanceNode($idlist); }
;
ifstmt: tIF expr tTHEN stmt {
NodeList *stmtlist = new NodeList;
stmtlist->push_back($stmt);
$$ = new IfStmtNode($expr, stmtlist); }
| tIF expr tTHEN '\n' stmtlist_insideif endif {
$$ = new IfStmtNode($expr, $stmtlist_insideif); }
;
ifelsestmt: tIF expr tTHEN stmt[stmt1] tELSE stmt[stmt2] {
NodeList *stmtlist1 = new NodeList;
stmtlist1->push_back($stmt1);
NodeList *stmtlist2 = new NodeList;
stmtlist2->push_back($stmt2);
$$ = new IfElseStmtNode($expr, stmtlist1, stmtlist2); }
| tIF expr tTHEN stmt[stmt1] tELSE '\n' stmtlist_insideif[stmtlist2] endif {
NodeList *stmtlist1 = new NodeList;
stmtlist1->push_back($stmt1);
$$ = new IfElseStmtNode($expr, stmtlist1, $stmtlist2); }
| tIF expr tTHEN '\n' stmtlist_insideif[stmtlist1] tELSE stmt[stmt2] {
NodeList *stmtlist2 = new NodeList;
stmtlist2->push_back($stmt2);
$$ = new IfElseStmtNode($expr, $stmtlist1, stmtlist2); }
| tIF expr tTHEN '\n' stmtlist_insideif[stmtlist1] tELSE '\n' stmtlist_insideif[stmtlist2] endif {
$$ = new IfElseStmtNode($expr, $stmtlist1, $stmtlist2); }
;
endif: /* empty */ {
LingoCompiler *compiler = g_lingo->_compiler;
warning("LingoCompiler::parse: no end if at line %d col %d in %s id: %d",
compiler->_linenumber, compiler->_colnumber, scriptType2str(compiler->_assemblyContext->_scriptType),
compiler->_assemblyContext->_id);
}
| tENDIF '\n' ;
loop: tREPEAT tWHILE expr '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWhileNode($expr, $stmtlist); }
| tREPEAT tWITH ID tEQ expr[start] tTO expr[end] '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWithToNode($ID, $start, false, $end, $stmtlist); }
| tREPEAT tWITH ID tEQ expr[start] tDOWN tTO expr[end] '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWithToNode($ID, $start, true, $end, $stmtlist); }
| tREPEAT tWITH ID tIN expr '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWithInNode($ID, $expr, $stmtlist); }
;
tell: tTELL expr tTO stmtoneliner {
NodeList *stmtlist = new NodeList;
stmtlist->push_back($stmtoneliner);
$$ = new TellNode($expr, stmtlist); }
| tTELL expr '\n' stmtlist tENDTELL '\n' {
$$ = new TellNode($expr, $stmtlist); }
;
when: tWHEN '\n' { $$ = new WhenNode($tWHEN.eventName, $tWHEN.stmt); } ;
stmtlist: /* empty */ { $$ = new NodeList; }
| nonemptystmtlist
;
nonemptystmtlist:
stmtlistline[item] {
NodeList *list = new NodeList;
if ($item) {
list->push_back($item);
}
$$ = list; }
| nonemptystmtlist[prev] stmtlistline[item] {
if ($item) {
$prev->push_back($item);
}
$$ = $prev; }
;
stmtlistline: '\n' { $$ = nullptr; }
| stmt
;
stmtlist_insideif: /* empty */ { $$ = new NodeList; }
| nonemptystmtlist_insideif
;
nonemptystmtlist_insideif:
stmtlistline_insideif[item] {
NodeList *list = new NodeList;
if ($item) {
list->push_back($item);
}
$$ = list; }
| nonemptystmtlist_insideif[prev] stmtlistline_insideif[item] {
if ($item) {
$prev->push_back($item);
}
$$ = $prev; }
;
stmtlistline_insideif: '\n' { $$ = nullptr; }
| stmt_insideif
;
// EXPRESSION
simpleexpr_nounarymath:
tINT { $$ = new IntNode($tINT); }
| tFLOAT { $$ = new FloatNode($tFLOAT); }
| tSYMBOL { $$ = new SymbolNode($tSYMBOL); } // D3
| tSTRING { $$ = new StringNode($tSTRING); }
| tNOT simpleexpr[arg] %prec tUNARY { $$ = new UnaryOpNode(LC::c_not, $arg); }
| ID '(' ')' { $$ = new FuncNode($ID, new NodeList); }
| ID '(' nonemptyexprlist[args] trailingcomma ')' { $$ = new FuncNode($ID, $args); }
| ID '(' var[method] expr_nounarymath trailingcomma ')' {
// This matches `obj(method arg)`
NodeList *args = new NodeList;
args->push_back($method);
args->push_back($expr_nounarymath);
$$ = new FuncNode($ID, args); }
| ID '(' var[method] expr_nounarymath ',' nonemptyexprlist[args] trailingcomma ')' {
// This matches `obj(method arg, ...)`
$args->insert_at(0, $expr_nounarymath);
$args->insert_at(0, $method);
$$ = new FuncNode($ID, $args); }
| '(' expr ')' { $$ = $expr; } ;
| var
| chunk
| object
| the
| list
;
var: ID { $$ = new VarNode($ID); } ;
varorchunk: var
| chunk
;
varorthe: var
| writablethe
;
chunk: tFIELD refargs { $$ = new FuncNode(new Common::String("field"), $refargs); }
| tCAST refargs { $$ = new FuncNode(new Common::String("cast"), $refargs); }
| tMEMBER refargs { $$ = new FuncNode(new Common::String("member"), $refargs); }
| tCASTLIB refargs { $$ = new FuncNode(new Common::String("castLib"), $refargs); }
| tCHAR expr[idx] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkChar, $idx, nullptr, $src); }
| tCHAR expr[start] tTO expr[end] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkChar, $start, $end, $src); }
| tWORD expr[idx] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkWord, $idx, nullptr, $src); }
| tWORD expr[start] tTO expr[end] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkWord, $start, $end, $src); }
| tITEM expr[idx] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkItem, $idx, nullptr, $src); }
| tITEM expr[start] tTO expr[end] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkItem, $start, $end, $src); }
| tLINE expr[idx] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkLine, $idx, nullptr, $src); }
| tLINE expr[start] tTO expr[end] tOF simpleexpr[src] {
$$ = new ChunkExprNode(kChunkLine, $start, $end, $src); }
| tTHE tLAST chunktype inof simpleexpr { $$ = new TheLastNode($chunktype, $simpleexpr); }
;
chunktype: tCHAR { $$ = kChunkChar; }
| tWORD { $$ = kChunkWord; }
| tITEM { $$ = kChunkItem; }
| tLINE { $$ = kChunkLine; }
;
object: tSCRIPT refargs { $$ = new FuncNode(new Common::String("script"), $refargs); }
| tWINDOW refargs { $$ = new FuncNode(new Common::String("window"), $refargs); }
;
refargs: simpleexpr {
// This matches `ref arg` and `ref(arg)`
NodeList *args = new NodeList;
args->push_back($simpleexpr);
$$ = args; }
| '(' ')' {
// This matches `ref()`
$$ = new NodeList; }
| '(' expr ',' ')' {
// This matches `ref(arg,)`
NodeList *args = new NodeList;
args->push_back($expr);
$$ = args; }
| '(' expr ',' nonemptyexprlist[args] trailingcomma ')' {
// This matches `ref(arg, ...)`
$args->insert_at(0, $expr);
$$ = $args; }
;
the: tTHE ID { $$ = new TheNode($ID); }
| tTHE ID tOF theobj { $$ = new TheOfNode($ID, $theobj); }
| tTHE tNUMBER tOF theobj { $$ = new TheOfNode(new Common::String("number"), $theobj); }
| thedatetime
| thenumberof
;
theobj: simpleexpr
| menu
| tMENUITEM simpleexpr[item] tOF tMENU simpleexpr[menu] { $$ = new MenuItemNode($item, $menu); }
| tSOUND simpleexpr[arg] { $$ = new SoundNode($arg); }
| tSPRITE simpleexpr[arg] { $$ = new SpriteNode($arg); }
;
menu: tMENU simpleexpr[arg] { $$ = new MenuNode($arg); } ;
thedatetime: tTHE tABBREVIATED tDATE { $$ = new TheDateTimeNode(kTheAbbr, kTheDate); }
| tTHE tABBREVIATED tTIME { $$ = new TheDateTimeNode(kTheAbbr, kTheTime); }
| tTHE tABBREV tDATE { $$ = new TheDateTimeNode(kTheAbbr, kTheDate); }
| tTHE tABBREV tTIME { $$ = new TheDateTimeNode(kTheAbbr, kTheTime); }
| tTHE tABBR tDATE { $$ = new TheDateTimeNode(kTheAbbr, kTheDate); }
| tTHE tABBR tTIME { $$ = new TheDateTimeNode(kTheAbbr, kTheTime); }
| tTHE tLONG tDATE { $$ = new TheDateTimeNode(kTheLong, kTheDate); }
| tTHE tLONG tTIME { $$ = new TheDateTimeNode(kTheLong, kTheTime); }
| tTHE tSHORT tDATE { $$ = new TheDateTimeNode(kTheShort, kTheDate); }
| tTHE tSHORT tTIME { $$ = new TheDateTimeNode(kTheShort, kTheTime); }
;
thenumberof:
tTHE tNUMBER tOF tCHARS inof simpleexpr { $$ = new TheNumberOfNode(kNumberOfChars, $simpleexpr); }
| tTHE tNUMBER tOF tWORDS inof simpleexpr { $$ = new TheNumberOfNode(kNumberOfWords, $simpleexpr); }
| tTHE tNUMBER tOF tITEMS inof simpleexpr { $$ = new TheNumberOfNode(kNumberOfItems, $simpleexpr); }
| tTHE tNUMBER tOF tLINES inof simpleexpr { $$ = new TheNumberOfNode(kNumberOfLines, $simpleexpr); }
| tTHE tNUMBER tOF tMENUITEMS inof menu { $$ = new TheNumberOfNode(kNumberOfMenuItems, $menu); }
| tTHE tNUMBER tOF tMENUS { $$ = new TheNumberOfNode(kNumberOfMenus, nullptr); }
| tTHE tNUMBER tOF tXTRAS { $$ = new TheNumberOfNode(kNumberOfXtras, nullptr); } // D5
| tTHE tNUMBER tOF tCASTLIBS { $$ = new TheNumberOfNode(kNumberOfCastlibs, nullptr); } // D5
;
inof: tIN | tOF ;
writablethe: tTHE ID { $$ = new TheNode($ID); }
| tTHE ID tOF writabletheobj { $$ = new TheOfNode($ID, $writabletheobj); }
;
writabletheobj: simpleexpr
| tMENU expr_noeq[arg] { $$ = new MenuNode($arg); } ;
| tMENUITEM expr_noeq[item] tOF tMENU expr_noeq[menu] { $$ = new MenuItemNode($item, $menu); }
| tSOUND expr_noeq[arg] { $$ = new SoundNode($arg); }
| tSPRITE expr_noeq[arg] { $$ = new SpriteNode($arg); }
;
list: '[' exprlist ']' { $$ = new ListNode($exprlist); }
| '[' ':' ']' { $$ = new PropListNode(new NodeList); }
| '[' proplist ']' { $$ = new PropListNode($proplist); }
;
// A property list must start with a proppair, but it may be followed by
// keyless expressions, which will be compiled as equivalent to the
// proppair <index>: <expr>.
proplist: proppair[item] {
NodeList *list = new NodeList;
list->push_back($item);
$$ = list; }
| proplist[prev] ',' proppair[item] {
$prev->push_back($item);
$$ = $prev; }
| proplist[prev] ',' expr[item] {
$prev->push_back($item);
$$ = $prev; }
;
proppair: tSYMBOL ':' expr { $$ = new PropPairNode(new SymbolNode($tSYMBOL), $expr); }
| ID ':' expr { $$ = new PropPairNode(new SymbolNode($ID), $expr); }
| tSTRING ':' expr { $$ = new PropPairNode(new StringNode($tSTRING), $expr); }
| tINT ':' expr { $$ = new PropPairNode(new IntNode($tINT), $expr); }
| tFLOAT ':' expr { $$ = new PropPairNode(new FloatNode($tFLOAT), $expr); }
;
unarymath: '+' simpleexpr[arg] %prec tUNARY { $$ = $arg; }
| '-' simpleexpr[arg] %prec tUNARY { $$ = new UnaryOpNode(LC::c_negate, $arg); }
;
simpleexpr: simpleexpr_nounarymath
| unarymath
;
// REMEMBER TO SYNC THIS WITH expr_nounarymath and expr_noeq!
expr: simpleexpr
| sprite
| expr[a] '+' expr[b] { $$ = new BinaryOpNode(LC::c_add, $a, $b); }
| expr[a] '-' expr[b] { $$ = new BinaryOpNode(LC::c_sub, $a, $b); }
| expr[a] '*' expr[b] { $$ = new BinaryOpNode(LC::c_mul, $a, $b); }
| expr[a] '/' expr[b] { $$ = new BinaryOpNode(LC::c_div, $a, $b); }
| expr[a] tMOD expr[b] { $$ = new BinaryOpNode(LC::c_mod, $a, $b); }
| expr[a] '>' expr[b] { $$ = new BinaryOpNode(LC::c_gt, $a, $b); }
| expr[a] '<' expr[b] { $$ = new BinaryOpNode(LC::c_lt, $a, $b); }
| expr[a] tEQ expr[b] { $$ = new BinaryOpNode(LC::c_eq, $a, $b); }
| expr[a] tNEQ expr[b] { $$ = new BinaryOpNode(LC::c_neq, $a, $b); }
| expr[a] tGE expr[b] { $$ = new BinaryOpNode(LC::c_ge, $a, $b); }
| expr[a] tLE expr[b] { $$ = new BinaryOpNode(LC::c_le, $a, $b); }
| expr[a] tAND expr[b] { $$ = new BinaryOpNode(LC::c_and, $a, $b); }
| expr[a] tOR expr[b] { $$ = new BinaryOpNode(LC::c_or, $a, $b); }
| expr[a] '&' expr[b] { $$ = new BinaryOpNode(LC::c_ampersand, $a, $b); }
| expr[a] tCONCAT expr[b] { $$ = new BinaryOpNode(LC::c_concat, $a, $b); }
| expr[a] tCONTAINS expr[b] { $$ = new BinaryOpNode(LC::c_contains, $a, $b); }
| expr[a] tSTARTS expr[b] { $$ = new BinaryOpNode(LC::c_starts, $a, $b); }
;
// This is the same as expr except it can't start with a unary math operator.
// It's ugly but unfortunately necessary to allow two expressions in a row with no delimeter.
// Without this, `cmd 1 + 1` could be interpreted as either `cmd(1 + 1)` or `cmd(1, +1)`.
// We only want to allow the first interpretation, so we must exclude unary math from the second expression.
expr_nounarymath: simpleexpr_nounarymath
| sprite
| expr_nounarymath[a] '+' expr[b] { $$ = new BinaryOpNode(LC::c_add, $a, $b); }
| expr_nounarymath[a] '-' expr[b] { $$ = new BinaryOpNode(LC::c_sub, $a, $b); }
| expr_nounarymath[a] '*' expr[b] { $$ = new BinaryOpNode(LC::c_mul, $a, $b); }
| expr_nounarymath[a] '/' expr[b] { $$ = new BinaryOpNode(LC::c_div, $a, $b); }
| expr_nounarymath[a] tMOD expr[b] { $$ = new BinaryOpNode(LC::c_mod, $a, $b); }
| expr_nounarymath[a] '>' expr[b] { $$ = new BinaryOpNode(LC::c_gt, $a, $b); }
| expr_nounarymath[a] '<' expr[b] { $$ = new BinaryOpNode(LC::c_lt, $a, $b); }
| expr_nounarymath[a] tEQ expr[b] { $$ = new BinaryOpNode(LC::c_eq, $a, $b); }
| expr_nounarymath[a] tNEQ expr[b] { $$ = new BinaryOpNode(LC::c_neq, $a, $b); }
| expr_nounarymath[a] tGE expr[b] { $$ = new BinaryOpNode(LC::c_ge, $a, $b); }
| expr_nounarymath[a] tLE expr[b] { $$ = new BinaryOpNode(LC::c_le, $a, $b); }
| expr_nounarymath[a] tAND expr[b] { $$ = new BinaryOpNode(LC::c_and, $a, $b); }
| expr_nounarymath[a] tOR expr[b] { $$ = new BinaryOpNode(LC::c_or, $a, $b); }
| expr_nounarymath[a] '&' expr[b] { $$ = new BinaryOpNode(LC::c_ampersand, $a, $b); }
| expr_nounarymath[a] tCONCAT expr[b] { $$ = new BinaryOpNode(LC::c_concat, $a, $b); }
| expr_nounarymath[a] tCONTAINS expr[b] { $$ = new BinaryOpNode(LC::c_contains, $a, $b); }
| expr_nounarymath[a] tSTARTS expr[b] { $$ = new BinaryOpNode(LC::c_starts, $a, $b); }
;
expr_noeq: simpleexpr
| sprite
| expr_noeq[a] '+' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_add, $a, $b); }
| expr_noeq[a] '-' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_sub, $a, $b); }
| expr_noeq[a] '*' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_mul, $a, $b); }
| expr_noeq[a] '/' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_div, $a, $b); }
| expr_noeq[a] tMOD expr_noeq[b] { $$ = new BinaryOpNode(LC::c_mod, $a, $b); }
| expr_noeq[a] '>' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_gt, $a, $b); }
| expr_noeq[a] '<' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_lt, $a, $b); }
| expr_noeq[a] tNEQ expr_noeq[b] { $$ = new BinaryOpNode(LC::c_neq, $a, $b); }
| expr_noeq[a] tGE expr_noeq[b] { $$ = new BinaryOpNode(LC::c_ge, $a, $b); }
| expr_noeq[a] tLE expr_noeq[b] { $$ = new BinaryOpNode(LC::c_le, $a, $b); }
| expr_noeq[a] tAND expr_noeq[b] { $$ = new BinaryOpNode(LC::c_and, $a, $b); }
| expr_noeq[a] tOR expr_noeq[b] { $$ = new BinaryOpNode(LC::c_or, $a, $b); }
| expr_noeq[a] '&' expr_noeq[b] { $$ = new BinaryOpNode(LC::c_ampersand, $a, $b); }
| expr_noeq[a] tCONCAT expr_noeq[b] { $$ = new BinaryOpNode(LC::c_concat, $a, $b); }
| expr_noeq[a] tCONTAINS expr_noeq[b] { $$ = new BinaryOpNode(LC::c_contains, $a, $b); }
| expr_noeq[a] tSTARTS expr_noeq[b] { $$ = new BinaryOpNode(LC::c_starts, $a, $b); }
;
sprite: tSPRITE expr tINTERSECTS simpleexpr { $$ = new IntersectsNode($expr, $simpleexpr); }
| tSPRITE expr tWITHIN simpleexpr { $$ = new WithinNode($expr, $simpleexpr); }
;
exprlist: /* empty */ { $$ = new NodeList; }
| nonemptyexprlist
;
nonemptyexprlist: expr[item] {
NodeList *list = new NodeList;
list->push_back($item);
$$ = list; }
| nonemptyexprlist[prev] ',' expr[item] {
$prev->push_back($item);
$$ = $prev; }
;
%%
int yyreport_syntax_error(const yypcontext_t *ctx) {
int res = 0;
Common::String msg = "syntax error, ";
// Report the unexpected token.
yysymbol_kind_t lookahead = yypcontext_token(ctx);
if (lookahead != YYSYMBOL_YYEMPTY)
msg += Common::String::format("unexpected %s", yysymbol_name(lookahead));
// Report the tokens expected at this point.
enum { TOKENMAX = 10 };
yysymbol_kind_t expected[TOKENMAX];
int n = yypcontext_expected_tokens(ctx, expected, TOKENMAX);
if (n < 0)
// Forward errors to yyparse.
res = n;
else
for (int i = 0; i < n; ++i)
msg += Common::String::format("%s %s", i == 0 ? ": expected" : " or", yysymbol_name(expected[i]));
yyerror(msg.c_str());
return res;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,335 @@
%top{
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define YY_NO_UNISTD_H
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
#define FORBIDDEN_SYMBOL_EXCEPTION_fread
#define FORBIDDEN_SYMBOL_EXCEPTION_stdin
#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
#define FORBIDDEN_SYMBOL_EXCEPTION_getc
#include "common/str.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-ast.h"
#include "director/lingo/lingo-codegen.h"
#include "director/lingo/lingo-gr.h"
#include "director/lingo/lingo-the.h"
using namespace Director;
static const char *inputbuffer;
static uint inputlen;
}
%option noyywrap
%option noinput
%option nounput
%option never-interactive
%option case-insensitive
%option outfile="engines/director/lingo/lingo-lex.cpp"
%{
// Push lines in stack
static void pushLine(uint num) {
LingoCompiler *compiler = g_lingo->_compiler;
if (num > inputlen)
return;
compiler->_lines[2] = compiler->_lines[1];
compiler->_lines[1] = compiler->_lines[0];
compiler->_lines[0] = &inputbuffer[num];
}
static void count() {
LingoCompiler *compiler = g_lingo->_compiler;
if (debugChannelSet(-1, kDebugParse))
debug("LEXER: Read '%s' at %d:%d", yytext, compiler->_linenumber, compiler->_colnumber);
char *p = yytext;
while (*p) {
if (*p == '\n') {
compiler->_linenumber++;
compiler->_colnumber = 0;
pushLine(compiler->_bytenumber + 1);
} else if (*p == '\xC2' && *(p + 1) == '\xAC') { // continuation
compiler->_linenumber++;
compiler->_colnumber = 0;
} else {
compiler->_colnumber++;
}
p++;
compiler->_bytenumber++;
}
}
static Common::String *cleanupString(const char *s) {
Common::String *res = new Common::String;
while (*s) {
if (*s == '\xC2' && *(s + 1) == '\xAC') { // continuation
s += 2;
*res += ' '; // replace with space
continue;
}
*res += *s;
s++;
}
return res;
}
static void skipWhitespace(const char **ptr) {
while (true) {
if (**ptr == ' ' || **ptr == '\t') {
*ptr += 1;
} else if (**ptr == '\xC2' && *(*ptr + 1) == '\xAC') { // continuation
*ptr += 2;
} else {
break;
}
}
}
static Common::String *readUntilWhitespace(const char **ptr) {
Common::String *res = new Common::String;
while (true) {
if (**ptr == ' ' || **ptr == '\t') {
break;
}
if (**ptr == '\xC2' && *(*ptr + 1) == '\xAC') { // continuation
break;
}
*res += **ptr;
*ptr += 1;
}
return res;
}
static Common::String *readUntilNewline(const char **ptr) {
Common::String *res = new Common::String;
while ((**ptr != '\n') && (**ptr != '\r') && (**ptr != '\0')) {
if (**ptr == '\xC2' && *(*ptr + 1) == '\xAC') { // continuation
*ptr += 2;
*res += ' '; // replace with space
continue;
}
*res += **ptr;
*ptr += 1;
}
return res;
}
%}
identifier [_[:alpha:]][_\.[:alnum:]]*
constfloat [[:digit:]]+\.[[:digit:]]*
constinteger [[:digit:]]+
conststring \"[^\"\r\n]*\"
operator [-+*/%^:,()><&\[\]]
newline (" "|\t|\xC2\xAC)*[\n\r]
spc (" "|\t|\xC2\xAC)
eventname (keyDown|keyUp|mouseDown|mouseUp|timeOut)
unparsedstmt [^\r\n]*
%%
{spc}+ { count(); }
[#]{identifier} { count(); yylval.s = new Common::String(yytext + 1); return tSYMBOL; } // D3, skip '#'
abbreviated { count(); return tABBREVIATED; }
abbrev { count(); return tABBREV; }
abbr { count(); return tABBR; }
after { count(); return tAFTER; } // D3
and { count(); return tAND; }
before { count(); return tBEFORE; } // D3
cast { count(); return tCAST; }
castlib { count(); return tCASTLIB; } // D5
castlibs { count(); return tCASTLIBS; } // D5
char { count(); return tCHAR; } // D3
chars { count(); return tCHARS; }
contains { count(); return tCONTAINS; }
date { count(); return tDATE; }
delete { count(); return tDELETE; }
down { count(); return tDOWN; }
else { count(); return tELSE; }
end({spc}+{identifier})? {
count();
const char *ptr = &yytext[3]; // Skip 'end '
skipWhitespace(&ptr);
if (!scumm_stricmp(ptr, "if"))
return tENDIF;
else if (!scumm_stricmp(ptr, "repeat"))
return tENDREPEAT;
else if (!scumm_stricmp(ptr, "tell"))
return tENDTELL;
yylval.s = new Common::String(ptr);
return tENDCLAUSE;
}
exit { count(); return tEXIT; }
^{spc}*factory { count(); return tFACTORY; }
field { count(); return tFIELD; }
frame { count(); return tFRAME; }
global { count(); return tGLOBAL; }
go({spc}+to)? { count(); return tGO; }
hilite { count(); return tHILITE; }
if { count(); return tIF; }
instance { count(); return tINSTANCE; }
intersects { count(); return tINTERSECTS;}
into { count(); return tINTO; }
in { count(); return tIN; }
item { count(); return tITEM; }
items { count(); return tITEMS; }
last { count(); return tLAST; }
line { count(); return tLINE; }
lines { count(); return tLINES; }
long { count(); return tLONG; }
macro { count(); return tMACRO; }
member { count(); return tMEMBER; }
menu { count(); return tMENU; }
menus { count(); return tMENUS; }
menuItem { count(); return tMENUITEM;}
menuItems { count(); return tMENUITEMS; }
method { count(); return tMETHOD; }
mod { count(); return tMOD;}
movie { count(); return tMOVIE; }
next { count(); return tNEXT; }
not { count(); return tNOT; }
number { count(); return tNUMBER; }
of { count(); return tOF; }
on { count(); return tON; } // D3
open { count(); return tOPEN; }
or { count(); return tOR; }
play { count(); return tPLAY; }
previous { count(); return tPREVIOUS; }
property { count(); return tPROPERTY; } // D4
put { count(); return tPUT; }
repeat { count(); return tREPEAT; }
return { count(); return tRETURN; }
script { count(); return tSCRIPT; }
scummvmAssertError { count(); return tASSERTERROR; }
set { count(); return tSET; }
short { count(); return tSHORT; }
sound { count(); return tSOUND; }
sprite { count(); return tSPRITE; }
starts { count(); return tSTARTS; }
tell { count(); return tTELL; }
the { count(); return tTHE; }
then { count(); return tTHEN; }
time { count(); return tTIME; }
to { count(); return tTO; }
when{spc}+{eventname}{spc}+then{spc}*{unparsedstmt} {
count();
const char *ptr = &yytext[5]; // Skip 'when '
skipWhitespace(&ptr);
Common::String *eventName = readUntilWhitespace(&ptr);
skipWhitespace(&ptr);
ptr += 4; // Skip 'then'
skipWhitespace(&ptr);
Common::String *stmt = readUntilNewline(&ptr);
yylval.w.eventName = eventName;
yylval.w.stmt = stmt;
return tWHEN;
}
while { count(); return tWHILE; }
window { count(); return tWINDOW; }
with { count(); return tWITH; }
within { count(); return tWITHIN; }
word { count(); return tWORD; }
words { count(); return tWORDS; }
xtras { count(); return tXTRAS; } // D5
[<][>] { count(); return tNEQ; }
[>][=] { count(); return tGE; }
[<][=] { count(); return tLE; }
[&][&] { count(); return tCONCAT; }
[=] { count(); return tEQ; }
{identifier} {
count();
yylval.s = new Common::String(yytext);
return tVARID;
}
{constfloat} { count(); yylval.f = atof(yytext); return tFLOAT; }
{constinteger} { count(); yylval.i = strtol(yytext, NULL, 10); return tINT; }
{operator} { count(); return *yytext; }
{newline} { count(); return '\n'; }
{conststring} { count(); yylval.s = cleanupString(&yytext[1]); yylval.s->deleteLastChar(); return tSTRING; }
. { count(); }
%%
extern int yydebug;
namespace Director {
int LingoCompiler::parse(const char *code) {
inputbuffer = code;
_bytenumber = 0;
inputlen = strlen(code);
_lines[0] = _lines[1] = _lines[2] = code;
YY_BUFFER_STATE bp;
if (debugChannelSet(-1, kDebugParse))
yydebug = 1;
else
yydebug = 0;
yy_delete_buffer(YY_CURRENT_BUFFER);
bp = yy_scan_string(code);
yy_switch_to_buffer(bp);
yyparse();
yy_delete_buffer(bp);
return 0;
}
} // End of namespace Director

View File

@@ -0,0 +1,482 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* The MCI documentation is: https://learn.microsoft.com/en-us/windows/win32/multimedia/mci
* Based on sources from Wine: https://github.com/wine-mirror/wine/blob/master/dlls/winmm/mci.c
* Table structure and algorithms also described in: https://patentimages.storage.googleapis.com/ad/06/7a/48766ca9df6fbc/US6397263.pdf
*/
#include "audio/audiostream.h"
#include "audio/decoders/wave.h"
#include "common/file.h"
#include "director/director.h"
#include "director/score.h"
#include "director/sound.h"
#include "director/window.h"
namespace Director {
enum MCITokenType {
MCI_PLAY,
MCI_OPEN,
MCI_CLOSE,
MCI_STATUS,
MCI_RECORD,
MCI_SEEK,
MCI_STOP,
MCI_PAUSE,
MCI_GETDEVCAPS,
MCI_SYSINFO,
MCI_BREAK,
MCI_SOUND,
MCI_SAVE,
MCI_LOAD,
MCI_RESUME,
MCI_SET,
MCI_INFO,
};
enum MCIDataType {
MCI_COMMAND_HEAD,
MCI_END_COMMAND,
MCI_END_COMMAND_LIST,
MCI_CONSTANT,
MCI_END_CONSTANT,
MCI_RETURN,
MCI_FLAG,
MCI_INTEGER,
MCI_STRING,
MCI_RECT,
MCI_DWORD_PTR,
};
typedef struct MCITokenData {
MCIDataType type;
Common::String string;
int integer = 0;
} MCITokenData;
typedef struct MCICommand {
MCITokenType id;
uint flags = 0;
Common::String device; /* MCI device name */
Common::HashMap<Common::String, MCITokenData> parameters;
} MCICommand;
enum MCIError {
MCIERR_NO_ERROR,
MCIERR_UNRECOGNISED_COMMAND,
MCIERR_STRING_PARSE,
};
struct CmdTableRow {
const char *keystr;
int flag;
int num;
MCIDataType data_type;
};
static const CmdTableRow table[] = {
{"open" ,MCI_OPEN ,0 ,MCI_COMMAND_HEAD },
{"" ,MCI_INTEGER ,0 ,MCI_RETURN } ,
{"notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{"wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{"type" ,0x00002000L ,-1 ,MCI_STRING } ,
{"element" ,0x00000200L ,-1 ,MCI_STRING } ,
{"alias" ,0x00000400L ,-1 ,MCI_STRING } ,
{"shareable" ,0x00000100L ,-1 ,MCI_FLAG } ,
{"" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{"close" ,MCI_CLOSE ,0 ,MCI_COMMAND_HEAD },
{"notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{"wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{"" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{"play" ,MCI_PLAY ,0 ,MCI_COMMAND_HEAD },
{"notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{"wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{"from" ,0x00000004L ,-1 ,MCI_INTEGER } ,
{"to" ,0x00000008L ,-1 ,MCI_INTEGER } ,
{"" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{"status" ,MCI_STATUS ,0 ,MCI_COMMAND_HEAD },
{"" ,MCI_DWORD_PTR ,0 ,MCI_RETURN } ,
{"notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{"wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{"" ,0x00000100L ,-1 ,MCI_CONSTANT } ,
{"position" ,0x00000002L ,-1 ,MCI_INTEGER } ,
{"length" ,0x00000001L ,-1 ,MCI_INTEGER } ,
{"number of tracks",-1 ,0x00000003L,MCI_INTEGER } ,
{"ready" ,0x00000007L ,-1 ,MCI_INTEGER } ,
{"mode" ,0x00000004L ,-1 ,MCI_INTEGER } ,
{"time format" ,-1 ,0x00000006L,MCI_INTEGER } ,
{"current track" ,-1 ,0x00000008L,MCI_INTEGER } ,
{"" ,0x00000000L ,-1 ,MCI_END_CONSTANT },
{ "track" ,0x00000010L ,-1 ,MCI_INTEGER } ,
{ "start" ,0x00000200L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "record" ,MCI_RECORD ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "from" ,0x00000004L ,-1 ,MCI_INTEGER } ,
{ "to" ,0x00000008L ,-1 ,MCI_INTEGER } ,
{ "insert" ,0x00000100L ,-1 ,MCI_FLAG } ,
{ "overwrite" ,0x00000200L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "seek" ,MCI_SEEK ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "to start" ,0x00000100L ,-1 ,MCI_FLAG } ,
{ "to end" ,0x00000200L ,-1 ,MCI_FLAG } ,
{ "to" ,0x00000008L ,-1 ,MCI_INTEGER } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "stop" ,MCI_STOP ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "pause" ,MCI_PAUSE ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "capability" ,MCI_GETDEVCAPS,0 ,MCI_COMMAND_HEAD },
{ "" ,MCI_INTEGER ,0 ,MCI_RETURN } ,
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000100L ,-1 ,MCI_CONSTANT } ,
{ "can record" ,0x00000001L ,-1 ,MCI_INTEGER } ,
{ "has audio" ,0x00000002L ,-1 ,MCI_INTEGER } ,
{ "has video" ,0x00000003L ,-1 ,MCI_INTEGER } ,
{ "uses files" ,0x00000005L ,-1 ,MCI_INTEGER } ,
{ "compound device",0x00000006L ,-1 ,MCI_INTEGER } ,
{ "device type" ,0x00000004L ,-1 ,MCI_INTEGER } ,
{ "can eject" ,0x00000007L ,-1 ,MCI_INTEGER } ,
{ "can play" ,0x00000008L ,-1 ,MCI_INTEGER } ,
{ "can save" ,0x00000009L ,-1 ,MCI_INTEGER } ,
{ "" ,0x00000000L ,-1 ,MCI_END_CONSTANT },
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "info" ,MCI_INFO ,0 ,MCI_COMMAND_HEAD },
{ "" ,MCI_STRING ,0 ,MCI_RETURN } ,
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "product" ,0x00000100L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "set" ,MCI_SET ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "time format" ,0x00000400L ,-1 ,MCI_CONSTANT } ,
{ "milliseconds" ,0x00000000L ,-1 ,MCI_INTEGER } ,
{ "ms" ,0x00000000L ,-1 ,MCI_INTEGER } ,
{ "" ,0x00000000L ,-1 ,MCI_END_CONSTANT },
{ "door open" ,0x00000100L ,-1 ,MCI_FLAG } ,
{ "door closed" ,0x00000200L ,-1 ,MCI_FLAG } ,
{ "audio" ,0x00000800L ,-1 ,MCI_CONSTANT } ,
{ "all" ,0x00000000L ,-1 ,MCI_INTEGER } ,
{ "left" ,0x00000001L ,-1 ,MCI_INTEGER } ,
{ "right" ,0x00000002L ,-1 ,MCI_INTEGER } ,
{ "" ,0x00000000L ,-1 ,MCI_END_CONSTANT },
{ "video" ,0x00001000L ,-1 ,MCI_FLAG } ,
{ "on" ,0x00002000L ,-1 ,MCI_FLAG } ,
{ "off" ,0x00004000L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "sysinfo" ,MCI_SYSINFO ,0 ,MCI_COMMAND_HEAD },
{ "" ,MCI_STRING ,0 ,MCI_RETURN } ,
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "quantity" ,0x00000100L ,-1 ,MCI_FLAG } ,
{ "open" ,0x00000200L ,-1 ,MCI_FLAG } ,
{ "installname" ,0x00000800L ,-1 ,MCI_FLAG } ,
{ "name" ,0x00000400L ,-1 ,MCI_INTEGER } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "break" ,MCI_BREAK ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "on" ,0x00000100L ,-1 ,MCI_INTEGER } ,
{ "off" ,0x00000400L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "sound" ,MCI_SOUND ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "save" ,MCI_SAVE ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000100L ,-1 ,MCI_STRING } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "load" ,MCI_LOAD ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000100L ,-1 ,MCI_STRING } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
{ "resume" ,MCI_RESUME ,0 ,MCI_COMMAND_HEAD },
{ "notify" ,0x00000001L ,-1 ,MCI_FLAG } ,
{ "wait" ,0x00000002L ,-1 ,MCI_FLAG } ,
{ "" ,0x00000000L ,-1 ,MCI_END_COMMAND } ,
};
static MCIError getString(const Common::String &name, uint &idx, Common::String &str) {
uint i_end;
if (name[idx] == '"' || name[idx] == '\'') { /* Quoted string */
char quote = name[idx];
i_end = name.findFirstOf(quote, idx + 1);
if (i_end == Common::String::npos) {
return MCIERR_STRING_PARSE; /* Unterminated string */
}
str = name.substr(idx + 1, i_end - idx - 1);
idx = i_end + 2;
} else { /* No quotes, so just find the next space */
i_end = name.findFirstOf(' ', idx);
if (i_end == Common::String::npos) {
i_end = name.size();
}
str = name.substr(idx, i_end - idx);
idx = i_end + 1;
}
debugC(5, kDebugLingoExec, "get_string(): Got string \"%s\"", str.c_str());
return MCIERR_NO_ERROR;
}
static void createTokenList(Common::StringArray &tokenList, const Common::String &name) {
uint idx = 0;
while (idx < name.size()) {
Common::String str;
MCIError err = getString(name, idx, str);
if (err != MCIERR_NO_ERROR) {
break;
}
tokenList.push_back(str);
}
}
static MCIError parseMCICommand(const Common::String &name, MCICommand &parsedCmd) {
Common::StringArray token_list;
createTokenList(token_list, name);
uint i_token = 0;
int i_table = 0;
int tableStart = -1, tableEnd = -1;
Common::String &verb = token_list[0];
/* Find the table section corresponding to the command's verb. */
for (auto& cmd : table) {
if ((tableStart < 0) && (cmd.data_type == MCI_COMMAND_HEAD) && (cmd.keystr == verb)) {
tableStart = i_table;
} else if ((tableStart >= 0) && (cmd.data_type == MCI_END_COMMAND)) {
tableEnd = i_table;
break;
}
i_table++;
}
debugC(5, kDebugLingoExec, "parseMCICommand(): tableStart: %d, tableEnd: %d", tableStart, tableEnd);
if (tableStart == -1 || tableEnd == -1) {
warning("parseMCICommand(): Verb %s not found in table", verb.c_str());
return MCIERR_UNRECOGNISED_COMMAND;
}
auto cmd = table[tableStart];
parsedCmd.id = (MCITokenType)cmd.flag;
parsedCmd.flags = cmd.num;
/* The MCI device will ALWAYS be the second token. */
parsedCmd.device = token_list[1];
/* Parse the rest of the arguments */
i_token = 2;
while (i_token < token_list.size()) {
bool found = false;
bool inConst = false;
int flag, cflag = 0;
const CmdTableRow *cmdtable, *c_cmdtable = nullptr;
auto& token = token_list[i_token];
for (i_table = tableStart; i_table < tableEnd; i_table++) {
MCIDataType cmd_type = table[i_table].data_type;
flag = table[i_table].flag;
cmdtable = &table[i_table];
switch (cmd_type) {
case MCI_CONSTANT:
c_cmdtable = cmdtable;
inConst = true;
cflag = flag;
break;
case MCI_END_CONSTANT:
c_cmdtable = nullptr;
inConst = false;
cflag = 0;
break;
default:
break;
}
if (token != table[i_table].keystr)
continue;
found = true;
switch (cmd_type) {
case MCI_COMMAND_HEAD:
case MCI_RETURN:
case MCI_END_COMMAND:
case MCI_END_COMMAND_LIST:
case MCI_CONSTANT:
case MCI_END_CONSTANT:
break;
case MCI_FLAG:
parsedCmd.flags |= flag;
i_token++;
break;
case MCI_INTEGER: {
if (inConst) { /* Handle the case where we've hit a MCI_INTEGER which is inside a MCI_CONSTANT block. */
MCITokenData token_data;
if (parsedCmd.parameters.tryGetVal(c_cmdtable->keystr, token_data)) {
token_data.integer |= flag;
} else {
token_data.type = MCI_CONSTANT;
token_data.integer |= flag;
parsedCmd.parameters[c_cmdtable->keystr] = token_data;
}
parsedCmd.flags |= cflag;
inConst = false;
} else {
parsedCmd.flags |= flag;
MCITokenData token_data;
token_data.type = MCI_INTEGER;
token_data.integer = atoi(token_list[++i_token].c_str());
parsedCmd.parameters[token] = token_data;
}
i_token++;
break;
}
case MCI_STRING: {
parsedCmd.flags |= flag;
MCITokenData token_data;
token_data.type = MCI_STRING;
token_data.string = token_list[++i_token];
parsedCmd.parameters[token] = token_data;
i_token++;
break;
}
default:
warning("parseMCICommand(): Unhandled command type %d", cmd_type);
return MCIERR_UNRECOGNISED_COMMAND;
}
}
if (!found) {
warning("parseMCICommand(): Parameter %s not found in table", token_list[i_token].c_str());
return MCIERR_UNRECOGNISED_COMMAND;
}
}
return MCIERR_NO_ERROR;
}
void Lingo::func_mci(const Common::String &name) {
MCICommand parsedCmd;
parseMCICommand(name, parsedCmd);
switch (parsedCmd.id) {
case MCI_OPEN: {
Common::File *file = new Common::File();
if (!file->open(Common::Path(parsedCmd.device, g_director->_dirSeparator))) {
warning("func_mci(): Failed to open %s", parsedCmd.device.c_str());
delete file;
return;
}
parsedCmd.parameters["type"].string.toLowercase(); /* In the case the open command type has something like WaveAudio instead of waveaudio */
if (parsedCmd.parameters["type"].string == "waveaudio") {
Audio::AudioStream *sound = Audio::makeWAVStream(file, DisposeAfterUse::YES);
if (parsedCmd.parameters.contains("alias")) {
_audioAliases[parsedCmd.parameters["alias"].string] = sound;
} else {
delete sound;
}
} else {
warning("func_mci(): Unhandled audio type %s", parsedCmd.parameters["type"].string.c_str());
delete file;
}
}
break;
case MCI_PLAY: {
warning("func_mci(): MCI play file: %s, from: %d, to: %d", parsedCmd.device.c_str(), parsedCmd.parameters["from"].integer, parsedCmd.parameters["to"].integer);
if (!_audioAliases.contains(parsedCmd.device)) {
warning("func_mci(): Unknown alias %s", parsedCmd.device.c_str());
return;
}
uint32 from = parsedCmd.parameters["from"].integer;
uint32 to = parsedCmd.parameters.contains("to") ? parsedCmd.parameters["to"].integer : -1;
_vm->getCurrentWindow()->getSoundManager()->playMCI(*_audioAliases[parsedCmd.device], from, to);
}
break;
default:
warning("func_mci: Unhandled MCI command: %d", parsedCmd.id); /* TODO: Convert MCITokenType into string */
}
}
void Lingo::func_mciwait(const Common::String &name) {
warning("STUB: MCI wait file: %s", name.c_str());
}
} // End of namespace Director

View File

@@ -0,0 +1,995 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "graphics/macgui/mactext.h"
#include "director/director.h"
#include "director/movie.h"
#include "director/window.h"
#include "director/lingo/lingo-ast.h"
#include "director/lingo/lingo-code.h"
#include "director/lingo/lingo-the.h"
#include "director/lingo/xlibs/a/aiff.h"
#include "director/lingo/xlibs/a/applecdxobj.h"
#include "director/lingo/xlibs/a/askuser.h"
#include "director/lingo/xlibs/b/backdrop.h"
#include "director/lingo/xlibs/b/barakeobj.h"
#include "director/lingo/xlibs/b/batqt.h"
#include "director/lingo/xlibs/b/bimxobj.h"
#include "director/lingo/xlibs/b/blitpict.h"
#include "director/lingo/xlibs/b/blockthedrawingxobj.h"
#include "director/lingo/xlibs/c/cdromxobj.h"
#include "director/lingo/xlibs/c/closebleedwindowxcmd.h"
#include "director/lingo/xlibs/c/colorxobj.h"
#include "director/lingo/xlibs/c/colorcursorxobj.h"
#include "director/lingo/xlibs/c/consumer.h"
#include "director/lingo/xlibs/c/cursorxobj.h"
#include "director/lingo/xlibs/d/darkenscreen.h"
#include "director/lingo/xlibs/d/dateutil.h"
#include "director/lingo/xlibs/d/developerStack.h"
#include "director/lingo/xlibs/d/dialogsxobj.h"
#include "director/lingo/xlibs/d/dirutil.h"
#include "director/lingo/xlibs/d/dllglue.h"
#include "director/lingo/xlibs/d/dpwavi.h"
#include "director/lingo/xlibs/d/dpwqtw.h"
#include "director/lingo/xlibs/d/draw.h"
#include "director/lingo/xlibs/e/ednox.h"
#include "director/lingo/xlibs/e/eventq.h"
#include "director/lingo/xlibs/f/fadegammadownxcmd.h"
#include "director/lingo/xlibs/f/fadegammaupxcmd.h"
#include "director/lingo/xlibs/f/fadegammaxcmd.h"
#include "director/lingo/xlibs/f/fedracul.h"
#include "director/lingo/xlibs/f/feimasks.h"
#include "director/lingo/xlibs/f/feiprefs.h"
#include "director/lingo/xlibs/f/fileexists.h"
#include "director/lingo/xlibs/f/fileio.h"
#include "director/lingo/xlibs/f/findereventsxcmd.h"
#include "director/lingo/xlibs/f/findfolder.h"
#include "director/lingo/xlibs/f/findsys.h"
#include "director/lingo/xlibs/f/findwin.h"
#include "director/lingo/xlibs/f/flushxobj.h"
#include "director/lingo/xlibs/f/fplayxobj.h"
#include "director/lingo/xlibs/f/fsutil.h"
#include "director/lingo/xlibs/g/genutils.h"
#include "director/lingo/xlibs/g/getscreenrectsxfcn.h"
#include "director/lingo/xlibs/g/getscreensizexfcn.h"
#include "director/lingo/xlibs/g/getsoundinlevel.h"
#include "director/lingo/xlibs/g/gpid.h"
#include "director/lingo/xlibs/h/henry.h"
#include "director/lingo/xlibs/h/hitmap.h"
#include "director/lingo/xlibs/i/inixobj.h"
#include "director/lingo/xlibs/i/instobj.h"
#include "director/lingo/xlibs/j/jwxini.h"
#include "director/lingo/xlibs/i/iscd.h"
#include "director/lingo/xlibs/i/ispippin.h"
#include "director/lingo/xlibs/j/jitdraw3.h"
#include "director/lingo/xlibs/l/labeldrvxobj.h"
#include "director/lingo/xlibs/l/listdev.h"
#include "director/lingo/xlibs/m/maniacbg.h"
#include "director/lingo/xlibs/m/mapnavigatorxobj.h"
#include "director/lingo/xlibs/m/memcheckxobj.h"
#include "director/lingo/xlibs/m/memoryxobj.h"
#include "director/lingo/xlibs/m/misc.h"
#include "director/lingo/xlibs/m/miscx.h"
#include "director/lingo/xlibs/m/mmaskxobj.h"
#include "director/lingo/xlibs/m/mmovie.h"
#include "director/lingo/xlibs/m/moovxobj.h"
#include "director/lingo/xlibs/m/movemousejp.h"
#include "director/lingo/xlibs/m/movemousexobj.h"
#include "director/lingo/xlibs/m/movieidxxobj.h"
#include "director/lingo/xlibs/m/movutils.h"
#include "director/lingo/xlibs/m/msfile.h"
#include "director/lingo/xlibs/m/mystisle.h"
#include "director/lingo/xlibs/m/mazexobj.h"
#include "director/lingo/xlibs/o/openbleedwindowxcmd.h"
#include "director/lingo/xlibs/o/orthoplayxobj.h"
#include "director/lingo/xlibs/p/paco.h"
#include "director/lingo/xlibs/p/palxobj.h"
#include "director/lingo/xlibs/p/panel.h"
#include "director/lingo/xlibs/p/pharaohs.h"
#include "director/lingo/xlibs/p/popupmenuxobj.h"
#include "director/lingo/xlibs/p/porta.h"
#include "director/lingo/xlibs/p/prefpath.h"
#include "director/lingo/xlibs/p/printomatic.h"
#include "director/lingo/xlibs/p/processxobj.h"
#include "director/lingo/xlibs/p/putcurs.h"
#include "director/lingo/xlibs/p/playsoundmoviexobj.h"
#include "director/lingo/xlibs/q/qtmovie.h"
#include "director/lingo/xlibs/q/qtcatmovieplayerxobj.h"
#include "director/lingo/xlibs/q/qtvr.h"
#include "director/lingo/xlibs/q/quicktime.h"
#include "director/lingo/xlibs/r/registercomponent.h"
#include "director/lingo/xlibs/r/remixxcmd.h"
#include "director/lingo/xlibs/s/serialportxobj.h"
#include "director/lingo/xlibs/s/smallutil.h"
#include "director/lingo/xlibs/s/soundjam.h"
#include "director/lingo/xlibs/s/spacemgr.h"
#include "director/lingo/xlibs/s/stagetc.h"
#include "director/lingo/xlibs/s/syscolor.h"
#include "director/lingo/xlibs/s/savenrestorexobj.h"
#include "director/lingo/xlibs/t/tengu.h"
#include "director/lingo/xlibs/t/temnotaxobj.h"
#include "director/lingo/xlibs/u/unittest.h"
#include "director/lingo/xlibs/v/valkyrie.h"
#include "director/lingo/xlibs/v/versions.h"
#include "director/lingo/xlibs/v/videodiscxobj.h"
#include "director/lingo/xlibs/v/vmisonxfcn.h"
#include "director/lingo/xlibs/v/vmpresent.h"
#include "director/lingo/xlibs/v/volumelist.h"
#include "director/lingo/xlibs/v/voyagerxsound.h"
#include "director/lingo/xlibs/w/widgetxobj.h"
#include "director/lingo/xlibs/w/window.h"
#include "director/lingo/xlibs/w/wininfo.h"
#include "director/lingo/xlibs/w/winxobj.h"
#include "director/lingo/xlibs/x/xcmdglue.h"
#include "director/lingo/xlibs/x/xio.h"
#include "director/lingo/xlibs/x/xplayanim.h"
#include "director/lingo/xlibs/x/xplaypacoxfcn.h"
#include "director/lingo/xlibs/x/xsoundxfcn.h"
#include "director/lingo/xlibs/x/xwin.h"
#include "director/lingo/xlibs/y/yasix.h"
#include "director/lingo/xtras/a/audio.h"
#include "director/lingo/xtras/b/budapi.h"
#include "director/lingo/xtras/directsound.h"
#include "director/lingo/xtras/d/displayres.h"
#include "director/lingo/xtras/filextra.h"
#include "director/lingo/xtras/keypoll.h"
#include "director/lingo/xtras/masterapp.h"
#include "director/lingo/xtras/m/mui.h"
#include "director/lingo/xtras/m/mui.h"
#include "director/lingo/xtras/openurl.h"
#include "director/lingo/xtras/oscheck.h"
#include "director/lingo/xtras/qtvrxtra.h"
#include "director/lingo/xtras/r/registryreader.h"
#include "director/lingo/xtras/rtk.h"
#include "director/lingo/xtras/scrnutil.h"
#include "director/lingo/xtras/s/smacker.h"
#include "director/lingo/xtras/s/staytoonedhall.h"
#include "director/lingo/xtras/s/staytoonedball.h"
#include "director/lingo/xtras/s/staytoonedglop.h"
#include "director/lingo/xtras/s/staytoonedhigh.h"
#include "director/lingo/xtras/s/staytoonedober.h"
#include "director/lingo/xtras/s/staytoonedtoon.h"
#include "director/lingo/xtras/timextra.h"
#include "director/lingo/xtras/xsound.h"
namespace Director {
static const struct PredefinedProto {
const char *name;
void (*func)(int);
int minArgs; // -1 -- arglist
int maxArgs;
int type;
int version;
} predefinedMethods[] = {
// all except window
{ "new", LM::m_new, -1, 0, kAllObj, 200 }, // D2
// factory and XObject
{ "describe", LM::m_describe, 0, 0, kXObj, 200 }, // D2
{ "dispose", LM::m_dispose, 0, 0, kFactoryObj | kXObj, 200 }, // D2
{ "get", LM::m_get, 1, 1, kFactoryObj, 200 }, // D2
{ "instanceRespondsTo", LM::m_instanceRespondsTo, 1, 1, kXObj, 300 }, // D3
{ "messageList", LM::m_messageList, 0, 0, kXObj, 300 }, // D3
{ "name", LM::m_name, 0, 0, kXObj, 300 }, // D3
{ "perform", LM::m_perform, -1, 0, kFactoryObj | kXObj, 300 }, // D3
{ "put", LM::m_put, 2, 2, kFactoryObj, 200 }, // D2
{ "respondsTo", LM::m_respondsTo, 1, 1, kXObj, 200 }, // D2
// script object and Xtra
{ "birth", LM::m_new, -1, 0, kScriptObj | kXtraObj, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0, 0 }
};
static const MethodProto windowMethods[] = {
// window / stage
{ "close", LM::m_close, 0, 0, 400 }, // D4
{ "forget", LM::m_forget, 0, 0, 400 }, // D4
{ "open", LM::m_open, 0, 0, 400 }, // D4
{ "moveToBack", LM::m_moveToBack, 0, 0, 400 }, // D4
{ "moveToFront", LM::m_moveToFront, 0, 0, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
void Lingo::initMethods() {
for (const PredefinedProto *mtd = predefinedMethods; mtd->name; mtd++) {
if (mtd->version > _vm->getVersion())
continue;
Symbol sym;
sym.name = new Common::String(mtd->name);
sym.type = HBLTIN;
sym.nargs = mtd->minArgs;
sym.maxArgs = mtd->maxArgs;
sym.targetType = mtd->type;
sym.u.bltin = mtd->func;
_methods[mtd->name] = sym;
}
Window::initMethods(windowMethods);
}
void Lingo::cleanupMethods() {
_methods.clear();
Window::cleanupMethods();
}
#define XLIBDEF(class, flags, version) \
{ #class, class::fileNames, class::open, class::close, flags, version }
static const struct XLibProto {
const char *className;
const XlibFileDesc *names;
XLibOpenerFunc opener;
XLibCloserFunc closer;
int type;
int version;
} xlibs[] = {
XLIBDEF(AiffXObj, kXObj, 400), // D4
XLIBDEF(AppleCDXObj, kXObj, 300), // D3
XLIBDEF(AskUser, kXObj, 400), // D4
XLIBDEF(AudioXtra, kXtraObj, 500), // D5
XLIBDEF(BackdropXObj, kXObj, 400), // D4
XLIBDEF(BarakeObj, kXObj, 400), // D4
XLIBDEF(BatQT, kXObj, 400), // D4
XLIBDEF(BIMXObj, kXObj, 400), // D4
XLIBDEF(BlitPictXObj, kXObj, 400), // D4
XLIBDEF(BlockTheDrawingXObj, kXObj, 400), // D4
XLIBDEF(BudAPIXtra, kXtraObj, 500), // D5
XLIBDEF(CDROMXObj, kXObj, 200), // D2
XLIBDEF(CloseBleedWindowXCMD,kXObj, 300), // D3
XLIBDEF(ColorXObj, kXObj, 400), // D4
XLIBDEF(ColorCursorXObj, kXObj, 400), // D4
XLIBDEF(ConsumerXObj, kXObj, 400), // D4
XLIBDEF(CursorXObj, kXObj, 400), // D4
XLIBDEF(DLLGlueXObj, kXObj, 400), // D4
XLIBDEF(DPWAVIXObj, kXObj, 300), // D3
XLIBDEF(DPWQTWXObj, kXObj, 300), // D3
XLIBDEF(DarkenScreen, kXObj, 300), // D3
XLIBDEF(DateUtilXObj, kXObj, 400), // D4
XLIBDEF(DeveloperStack, kXObj, 300), // D3
XLIBDEF(DialogsXObj, kXObj, 400), // D4
XLIBDEF(DirUtilXObj, kXObj, 400), // D4
XLIBDEF(DirectsoundXtra, kXtraObj, 500), // D5
XLIBDEF(DisplayResXtra, kXtraObj, 500), // D5
XLIBDEF(DrawXObj, kXObj, 400), // D4
XLIBDEF(Ednox, kXObj, 300), // D3
XLIBDEF(EventQXObj, kXObj, 400), // D4
XLIBDEF(FEDraculXObj, kXObj, 400), // D4
XLIBDEF(FEIMasksXObj, kXObj, 400), // D4
XLIBDEF(FEIPrefsXObj, kXObj, 400), // D4
XLIBDEF(FSUtilXObj, kXObj, 400), // D4
XLIBDEF(FadeGammaDownXCMD, kXObj, 400), // D4
XLIBDEF(FadeGammaUpXCMD, kXObj, 400), // D4
XLIBDEF(FadeGammaXCMD, kXObj, 400), // D4
XLIBDEF(FileExists, kXObj, 300), // D3
XLIBDEF(FileIO, kXObj | kXtraObj,200), // D2
XLIBDEF(FileXtra, kXtraObj, 500), // D5
XLIBDEF(FindFolder, kXObj, 300), // D3
XLIBDEF(FindSys, kXObj, 400), // D4
XLIBDEF(FindWin, kXObj, 400), // D4
XLIBDEF(FinderEventsXCMD, kXObj, 400), // D4
XLIBDEF(FlushXObj, kXObj, 300), // D3
XLIBDEF(FPlayXObj, kXObj, 200), // D2
XLIBDEF(GenUtilsXObj, kXObj, 400), // D4
XLIBDEF(GetScreenRectsXFCN, kXObj, 300), // D3
XLIBDEF(GetScreenSizeXFCN, kXObj, 300), // D3
XLIBDEF(GetSoundInLevelXObj,kXObj, 400), // D4
XLIBDEF(GpidXObj, kXObj, 400), // D4
XLIBDEF(HenryXObj, kXObj, 400), // D4
XLIBDEF(HitMap, kXObj, 400), // D4
XLIBDEF(IniXObj, kXObj, 400), // D4
XLIBDEF(InstObjXObj, kXObj, 400), // D4
XLIBDEF(IsCD, kXObj, 300), // D3
XLIBDEF(IsPippin, kXObj, 400), // D4
XLIBDEF(JITDraw3XObj, kXObj, 400), // D4
XLIBDEF(JourneyWareXINIXObj,kXObj, 400), // D4
XLIBDEF(KeypollXtra, kXtraObj, 500), // D5
XLIBDEF(LabelDrvXObj, kXObj, 400), // D4
XLIBDEF(ListDevXObj, kXObj, 500), // D5
XLIBDEF(MMovieXObj, kXObj, 400), // D4
XLIBDEF(ManiacBgXObj, kXObj, 300), // D3
XLIBDEF(MapNavigatorXObj, kXObj, 400), // D4
XLIBDEF(MasterAppXtra, kXtraObj, 500), // D5
XLIBDEF(MazeXObj, kXObj, 400), // D4
XLIBDEF(MemCheckXObj, kXObj, 400), // D4
XLIBDEF(MemoryXObj, kXObj, 300), // D3
XLIBDEF(Misc, kXObj, 400), // D4
XLIBDEF(MiscX, kXObj, 400), // D4
XLIBDEF(MMaskXObj, kXObj, 400), // D4
XLIBDEF(MoovXObj, kXObj, 300), // D3
XLIBDEF(MoveMouseJPXObj, kXObj, 400), // D4
XLIBDEF(MoveMouseXObj, kXObj, 400), // D4
XLIBDEF(MovieIdxXObj, kXObj, 400), // D4
XLIBDEF(MovUtilsXObj, kXObj, 400), // D4
XLIBDEF(MSFile, kXObj, 400), // D4
XLIBDEF(MuiXtra, kXtraObj, 500), // D5
XLIBDEF(MystIsleXObj, kXObj, 400), // D4
XLIBDEF(OSCheckXtra, kXtraObj, 400), // D4
XLIBDEF(OpenBleedWindowXCMD,kXObj, 300), // D3
XLIBDEF(OpenURLXtra, kXtraObj, 500), // D5
XLIBDEF(OrthoPlayXObj, kXObj, 400), // D4
XLIBDEF(PACoXObj, kXObj, 300), // D3
XLIBDEF(PalXObj, kXObj, 400), // D4
XLIBDEF(PanelXObj, kXObj, 200), // D2
XLIBDEF(PharaohsXObj, kXObj, 400), // D4
XLIBDEF(PlaySoundMovieXObj, kXObj, 400), // D4
XLIBDEF(PopUpMenuXObj, kXObj, 200), // D2
XLIBDEF(Porta, kXObj, 300), // D3
XLIBDEF(PrefPath, kXObj, 400), // D4
XLIBDEF(PrintOMaticXObj, kXObj | kXtraObj,400), // D4
XLIBDEF(ProcessXObj, kXObj, 400), // D4
XLIBDEF(PutcursXObj, kXObj, 400), // D4
XLIBDEF(QTCatMoviePlayerXObj,kXObj, 400), // D4
XLIBDEF(QTMovie, kXObj, 400), // D4
XLIBDEF(QTVR, kXObj, 400), // D4
XLIBDEF(QtvrxtraXtra, kXtraObj, 500), // D5
XLIBDEF(Quicktime, kXObj, 300), // D3
XLIBDEF(RearWindowXObj, kXObj, 400), // D4
XLIBDEF(RegisterComponent, kXObj, 400), // D4
XLIBDEF(RegistryReaderXtra, kXtraObj, 500), // D5
XLIBDEF(RemixXCMD, kXObj, 300), // D3
XLIBDEF(RolloverToolkitXtra,kXtraObj, 500), // D5
XLIBDEF(SaveNRestoreXObj, kXObj, 400), // D4
XLIBDEF(ScrnUtilXtra, kXtraObj, 500), // D5
XLIBDEF(SerialPortXObj, kXObj, 200), // D2
XLIBDEF(SmackerXtra, kXtraObj, 500), // D5
XLIBDEF(SmallUtilXObj, kXObj, 400), // D4
XLIBDEF(SoundJam, kXObj, 400), // D4
XLIBDEF(SpaceMgr, kXObj, 400), // D4
XLIBDEF(StageTCXObj, kXObj, 400), // D4
XLIBDEF(StayToonedBallXtra, kXtraObj, 500), // D5
XLIBDEF(StayToonedGlopXtra, kXtraObj, 500), // D5
XLIBDEF(StayToonedHallXtra, kXtraObj, 500), // D5
XLIBDEF(StayToonedHighXtra, kXtraObj, 500), // D5
XLIBDEF(StayToonedOberXtra, kXtraObj, 500), // D5
XLIBDEF(StayToonedToonXtra, kXtraObj, 500), // D5
XLIBDEF(SysColorXObj, kXObj, 400), // D4
XLIBDEF(TemnotaXObj, kXObj, 400), // D4
XLIBDEF(TenguXObj, kXObj, 400), // D4
XLIBDEF(TimextraXtra, kXtraObj, 500), // D5
XLIBDEF(UnitTestXObj, kXObj, 400), // D4
XLIBDEF(VMPresentXObj, kXObj, 400), // D4
XLIBDEF(VMisOnXFCN, kXObj, 400), // D4
XLIBDEF(ValkyrieXObj, kXObj, 400), // D4
XLIBDEF(VersionsXObj, kXObj, 400), // D4
XLIBDEF(VideodiscXObj, kXObj, 200), // D2
XLIBDEF(VolumeList, kXObj, 300), // D3
XLIBDEF(VoyagerXSoundXObj, kXObj, 400), // D4
XLIBDEF(WinInfoXObj, kXObj, 400), // D4
XLIBDEF(WidgetXObj, kXObj, 400), // D4
XLIBDEF(WindowXObj, kXObj, 200), // D2
XLIBDEF(XCMDGlueXObj, kXObj, 200), // D2
XLIBDEF(XPlayPACoXFCN, kXObj, 300), // D3
XLIBDEF(XSoundXFCN, kXObj, 400), // D4
XLIBDEF(XWINXObj, kXObj, 300), // D3
XLIBDEF(XioXObj, kXObj, 400), // D3
XLIBDEF(XPlayAnim, kXObj, 300), // D3
XLIBDEF(XsoundXtra, kXtraObj, 500), // D5
XLIBDEF(Yasix, kXObj, 300), // D3
{ nullptr, nullptr, nullptr, nullptr, 0, 0 }
};
void Lingo::initXLibs() {
Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> quirks;
for (const XLibProto *lib = xlibs; lib->names; lib++) {
if (lib->version > _vm->getVersion())
continue;
for (uint i = 0; lib->names[i].name; i++) {
bool isQuirk = false;
if (lib->names[i].gameId) {
isQuirk = strcmp(lib->names[i].gameId, g_director->getGameId()) == 0;
// If this entry belongs to a specific game, skip it unless matched
if (!isQuirk)
continue;
}
if (isQuirk) {
quirks[lib->names[i].name] = i;
} else if (quirks.contains(lib->names[i].name)) {
// Ignore new entries that conflict with per-game quirks
continue;
}
if (!isQuirk && _xlibOpeners.contains(lib->names[i].name))
warning("Lingo::initXLibs(): Duplicate entry for %s", lib->names[i].name);
debugC(5, kDebugLingoExec, "Lingo::initXLibs(): %s -> %s", lib->names[i].name, lib->className);
_xlibOpeners[lib->names[i].name] = lib->opener;
_xlibClosers[lib->names[i].name] = lib->closer;
_xlibTypes[lib->names[i].name] = lib->type;
}
}
}
void Lingo::cleanupXLibs() {
_xlibOpeners.clear();
_xlibClosers.clear();
}
Common::String Lingo::normalizeXLibName(Common::String name) {
// Normalize to remove machintosh path delimiters (':', '@:')
name = convertPath(name);
size_t pos = name.findLastOf(g_director->_dirSeparator);
if (pos != Common::String::npos)
name = name.substr(pos + 1, name.size());
Common::Platform platform = _vm->getPlatform();
if (platform == Common::kPlatformMacintosh || platform == Common::kPlatformMacintoshII) {
if (name.hasSuffixIgnoreCase(".xlib"))
name = name.substr(0, name.size() - 5);
} else if (platform == Common::kPlatformWindows) {
if (name.hasSuffixIgnoreCase(".dll"))
name = name.substr(0, name.size() - 4);
if (name.hasSuffixIgnoreCase(".x16"))
name = name.substr(0, name.size() - 4);
if (name.hasSuffixIgnoreCase(".x32"))
name = name.substr(0, name.size() - 4);
}
name.trim();
return name;
}
void Lingo::openXLib(Common::String name, ObjectType type, const Common::Path &path) {
name = normalizeXLibName(name);
if (_openXLibs.contains(name))
return;
if (type == 0 && _xlibTypes.contains(name)) {
type = (_xlibTypes[name] & kXtraObj) ? kXtraObj : kXObj;
}
// manual override for game quirks
if (name.equalsIgnoreCase("fileio")) {
if (g_director->_fileIOType == kXtraObj && g_director->getVersion() >= 500) {
type = kXtraObj;
} else if (g_director->_fileIOType == kXObj) {
type = kXObj;
}
}
_openXLibs[name] = type;
if (_xlibOpeners.contains(name)) {
(*_xlibOpeners[name])(type, path);
} else {
warning("Lingo::openXLib: Unimplemented xlib: '%s'", name.c_str());
}
}
void Lingo::closeXLib(Common::String name) {
name = normalizeXLibName(name);
if (!_openXLibs.contains(name)) {
warning("Lingo::closeXLib: xlib %s is not open", name.c_str());
return;
}
ObjectType type = _openXLibs[name];
_openXLibs.erase(name);
if (_xlibClosers.contains(name)) {
(*_xlibClosers[name])(type);
} else {
warning("Lingo::closeXLib: Unimplemented xlib: '%s'", name.c_str());
}
}
void Lingo::closeOpenXLibs() {
for (auto &it : _openXLibs) {
// does not affect Xtras
if (it._value == kXObj) {
closeXLib(it._key);
}
}
}
void Lingo::reloadOpenXLibs() {
OpenXLibsHash openXLibsCopy = _openXLibs;
for (auto &it : openXLibsCopy) {
closeXLib(it._key);
// FIXME: keep track of where the xlib path is
openXLib(it._key, it._value, Common::Path());
}
}
// Initialization/disposal
void LM::m_new(int nargs) {
// This is usually overridden by a user-defined mNew
//
// However, in behaviors it is often absent, and it is
// in essence our default constructor.
g_lingo->push(g_lingo->_state->me);
}
void LM::m_dispose(int nargs) {
g_lingo->_state->me.u.obj->dispose();
}
/* ScriptContext */
ScriptContext::ScriptContext(Common::String name, ScriptType type, int id, uint16 castLibHint, uint16 parentNumber, int scriptId)
: Object<ScriptContext>(name), _scriptType(type), _id(id), _castLibHint(castLibHint), _parentNumber(parentNumber), _scriptId(scriptId) {
_objType = kScriptObj;
}
ScriptContext::ScriptContext(const ScriptContext &sc) : Object<ScriptContext>(sc) {
_scriptType = sc._scriptType;
_functionNames = sc._functionNames;
for (auto &it : sc._functionHandlers) {
_functionHandlers[it._key] = it._value;
_functionHandlers[it._key].ctx = this;
}
for (auto &it : sc._eventHandlers) {
_eventHandlers[it._key] = it._value;
_eventHandlers[it._key].ctx = this;
}
_constants = sc._constants;
_properties = sc._properties;
_propertyNames = sc._propertyNames;
_parentNumber = sc._parentNumber;
_scriptId = sc._scriptId;
_id = sc._id;
_castLibHint = sc._castLibHint;
}
ScriptContext::~ScriptContext() {
}
Common::String ScriptContext::asString() {
return Common::String::format("script: %d \"%s\" %d %p", _id, _name.c_str(), _inheritanceLevel, (void *)this);
}
Symbol ScriptContext::define(const Common::String &name, ScriptData *code, Common::Array<Common::String> *argNames, Common::Array<Common::String> *varNames) {
Symbol sym;
sym.name = new Common::String(name);
sym.type = HANDLER;
sym.u.defn = code;
sym.nargs = argNames->size();
sym.maxArgs = argNames->size();
sym.argNames = argNames;
sym.varNames = varNames;
sym.ctx = this;
if (debugChannelSet(1, kDebugCompile)) {
debugC(1, kDebugCompile, "%s", g_lingo->formatFunctionBody(sym).c_str());
debugC(1, kDebugCompile, "<end define code>");
}
_functionHandlers[name] = sym;
if (g_lingo->_eventHandlerTypeIds.contains(name)) {
_eventHandlers[g_lingo->_eventHandlerTypeIds[name]] = sym;
}
return sym;
}
Symbol ScriptContext::getMethod(const Common::String &methodName) {
Symbol sym;
if (_functionHandlers.contains(methodName)) {
sym = _functionHandlers[methodName];
sym.target = this;
return sym;
}
sym = Object<ScriptContext>::getMethod(methodName);
if (sym.type != VOIDSYM)
return sym;
if (_objType == kScriptObj) {
if (_properties.contains("ancestor") && _properties["ancestor"].type == OBJECT
&& (_properties["ancestor"].u.obj->getObjType() & (kScriptObj | kXtraObj))) {
// ancestor method
sym = _properties["ancestor"].u.obj->getMethod(methodName);
if (sym.type != VOIDSYM)
debugC(3, kDebugLingoExec, "Calling method '%s' on ancestor: <%s>", methodName.c_str(), _properties["ancestor"].asString(true).c_str());
}
}
return sym;
}
bool ScriptContext::hasProp(const Common::String &propName) {
if (_disposed) {
error("Property '%s' accessed on disposed object <%s>", propName.c_str(), Datum(this).asString(true).c_str());
}
if (_properties.contains(propName)) {
return true;
}
if (_objType == kScriptObj) {
if (_properties.contains("ancestor") && _properties["ancestor"].type == OBJECT
&& (_properties["ancestor"].u.obj->getObjType() & (kScriptObj | kXtraObj))) {
return _properties["ancestor"].u.obj->hasProp(propName);
}
// This is used by behaviors
if (propName.equalsIgnoreCase("spriteNum")) {
return true;
}
}
return false;
}
Datum ScriptContext::getProp(const Common::String &propName) {
if (_disposed) {
error("Property '%s' accessed on disposed object <%s>", propName.c_str(), Datum(this).asString(true).c_str());
}
if (_properties.contains(propName)) {
return _properties[propName];
}
if (_objType == kScriptObj) {
if (_properties.contains("ancestor") && _properties["ancestor"].type == OBJECT
&& (_properties["ancestor"].u.obj->getObjType() & (kScriptObj | kXtraObj))) {
debugC(3, kDebugLingoExec, "Getting prop '%s' from ancestor: <%s>", propName.c_str(), _properties["ancestor"].asString(true).c_str());
return _properties["ancestor"].u.obj->getProp(propName);
}
// This is used by behaviors
if (propName.equalsIgnoreCase("spriteNum")) {
return Datum((int)g_director->getCurrentMovie()->_currentSpriteNum);
}
}
_propertyNames.push_back(propName);
return _properties[propName]; // return new property
}
Common::String ScriptContext::getPropAt(uint32 index) {
uint32 target = 1;
for (auto &it : _propertyNames) {
if (target == index) {
return it;
}
target += 1;
}
return Common::String();
}
uint32 ScriptContext::getPropCount() {
return _propertyNames.size();
}
void ScriptContext::setProp(const Common::String &propName, const Datum &value, bool force) {
if (_disposed) {
error("Property '%s' accessed on disposed object <%s>", propName.c_str(), Datum(this).asString(true).c_str());
}
if (_properties.contains(propName)) {
_properties[propName] = value;
return;
}
if (force) {
// used by e.g. the script compiler to add properties
_propertyNames.push_back(propName);
_properties[propName] = value;
} else if (_objType == kScriptObj) {
if (_properties.contains("ancestor") && _properties["ancestor"].type == OBJECT
&& (_properties["ancestor"].u.obj->getObjType() & (kScriptObj | kXtraObj))) {
debugC(3, kDebugLingoExec, "Getting prop '%s' from ancestor: <%s>", propName.c_str(), _properties["ancestor"].asString(true).c_str());
_properties["ancestor"].u.obj->setProp(propName, value, force);
}
} else if (_objType == kFactoryObj) {
// D3 style anonymous objects/factories, set whatever properties you like
_propertyNames.push_back(propName);
_properties[propName] = value;
}
}
Common::String ScriptContext::formatFunctionList(const char *prefix) {
Common::String result;
for (auto it = _functionHandlers.begin(); it != _functionHandlers.end(); ++it) {
result += Common::String::format("%s%s\n", prefix, g_lingo->formatFunctionName(it->_value).c_str());
}
return result;
}
// Object array
void LM::m_get(int nargs) {
ScriptContext *me = static_cast<ScriptContext *>(g_lingo->_state->me.u.obj);
Datum indexD = g_lingo->pop();
uint index = MAX(0, indexD.asInt());
if (me->_objArray.contains(index)) {
g_lingo->push(me->_objArray[index]);
} else {
g_lingo->push(Datum(0));
}
}
void LM::m_put(int nargs) {
ScriptContext *me = static_cast<ScriptContext *>(g_lingo->_state->me.u.obj);
Datum value = g_lingo->pop();
Datum indexD = g_lingo->pop();
uint index = MAX(0, indexD.asInt());
me->_objArray[index] = value;
}
// Other
void LM::m_perform(int nargs) {
bool allowRetVal = g_lingo->pop().asInt() != 0; // Pop allowRetVal that should be used for the LC::Call
// Lingo doesn't seem to bother cloning the object when
// mNew is called with mPerform
Datum d(g_lingo->_state->me);
AbstractObject *me = d.u.obj;
Datum methodName = g_lingo->_state->stack.remove_at(g_lingo->_state->stack.size() - nargs); // Take method name out of stack
Symbol funcSym = me->getMethod(*methodName.u.s);
// Object methods expect the first argument to be the object
g_lingo->_state->stack.insert_at(g_lingo->_state->stack.size() - nargs + 1, d);
LC::call(funcSym, nargs, allowRetVal);
if (allowRetVal) {
// If the method expects a return value, push dummy on stack
g_lingo->pushVoid();
}
}
// XObject
void LM::m_describe(int nargs) {
warning("STUB: m_describe");
}
void LM::m_instanceRespondsTo(int nargs) {
AbstractObject *me = g_lingo->_state->me.u.obj;
Datum d = g_lingo->pop();
Common::String methodName = d.asString();
if (me->getMethod(methodName).type != VOIDSYM) {
g_lingo->push(Datum(1));
} else {
g_lingo->push(Datum(0));
}
}
void LM::m_messageList(int nargs) {
warning("STUB: m_messageList");
g_lingo->push(Datum(""));
}
void LM::m_name(int nargs) {
AbstractObject *me = g_lingo->_state->me.u.obj;
g_lingo->push(me->getName());
}
void LM::m_respondsTo(int nargs) {
AbstractObject *me = g_lingo->_state->me.u.obj;
Datum d = g_lingo->pop();
Common::String methodName = d.asString();
// TODO: Check inheritance level
if (me->getMethod(methodName).type != VOIDSYM) {
g_lingo->push(Datum(1));
} else {
g_lingo->push(Datum(0));
}
}
// Window
Common::String Window::asString() {
return "window \"" + getName() + "\"";
}
bool Window::hasProp(const Common::String &propName) {
Common::String fieldName = Common::String::format("%d%s", kTheWindow, propName.c_str());
return g_lingo->_theEntityFields.contains(fieldName) && hasField(g_lingo->_theEntityFields[fieldName]->field);
}
Datum Window::getProp(const Common::String &propName) {
Common::String fieldName = Common::String::format("%d%s", kTheWindow, propName.c_str());
if (g_lingo->_theEntityFields.contains(fieldName)) {
return getField(g_lingo->_theEntityFields[fieldName]->field);
}
warning("Window::getProp: unknown property '%s'", propName.c_str());
return Datum();
}
void Window::setProp(const Common::String &propName, const Datum &value, bool force) {
Common::String fieldName = Common::String::format("%d%s", kTheWindow, propName.c_str());
if (g_lingo->_theEntityFields.contains(fieldName)) {
setField(g_lingo->_theEntityFields[fieldName]->field, value);
return;
}
warning("Window::setProp: unknown property '%s'", propName.c_str());
}
bool Window::hasField(int field) {
switch (field) {
case kTheDrawRect:
case kTheFileName:
case kTheModal:
case kTheRect:
case kTheSourceRect:
case kTheTitle:
case kTheTitleVisible:
case kTheVisible:
case kTheWindowType:
return true;
default:
break;
}
return false;
}
Datum Window::getField(int field) {
switch (field) {
case kTheTitle:
return _window->getTitle();
case kTheTitleVisible:
return _window->isTitleVisible();
case kTheVisible:
return _window->isVisible();
case kTheWindowType:
return getWindowType();
case kTheRect:
return getStageRect();
case kTheModal:
return getModal();
case kTheFileName:
return getFileName();
case kTheDrawRect:
warning("Window::getField: poorly handled getting field 'drawRect'");
ensureMovieIsLoaded();
// TODO: This should allow stretching or panning
return getStageRect();
case kTheSourceRect:
// case kTheImage:
// case kThePicture::
ensureMovieIsLoaded(); // Remove fallthrough once implemented
// fallthrough
default:
warning("Window::getField: unhandled field '%s'", g_lingo->field2str(field));
return Datum();
}
}
void Window::setField(int field, const Datum &value) {
switch (field) {
case kTheTitle:
setTitle(value.asString());
break;
case kTheTitleVisible:
setTitleVisible((bool)value.asInt());
break;
case kTheVisible:
setVisible((bool)value.asInt());
break;
case kTheWindowType:
setWindowType(value.asInt());
break;
case kTheDrawRect:
warning("Window::setField: poorly handled setting field 'drawRect'");
// fallthrough
case kTheRect:
setStageRect(value);
break;
case kTheModal:
setModal((bool)value.asInt());
break;
case kTheFileName:
setFileName(value.asString());
break;
default:
warning("Window::setField: unhandled field '%s'", g_lingo->field2str(field));
break;
}
}
void LM::m_close(int nargs) {
Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
me->setVisible(false);
}
void LM::m_forget(int nargs) {
Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
FArray *windowList = g_lingo->_windowList.u.farr;
int windowIndex = -1;
for (int i = 0; i < (int)windowList->arr.size(); i++) {
if (windowList->arr[i].type != OBJECT || windowList->arr[i].u.obj->getObjType() != kWindowObj)
continue;
Window *window = static_cast<Window *>(windowList->arr[i].u.obj);
if (window == me) {
windowIndex = i;
break;
}
}
if (windowIndex == -1) {
warning("m_forget: me object %s not found in window list", g_lingo->_state->me.asString().c_str());
return;
}
if (windowIndex < (int)windowList->arr.size())
windowList->arr.remove_at(windowIndex);
// remove me from global vars
for (auto &it : g_lingo->_globalvars) {
if (it._value.type != OBJECT || it._value.u.obj->getObjType() != kWindowObj)
continue;
if (it._value.u.obj == me)
g_lingo->_globalvars[it._key] = 0;
}
g_director->forgetWindow(me);
}
void LM::m_open(int nargs) {
Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
bool wasVisible = me->_window->isVisible();
me->setVisible(true);
if (!wasVisible)
me->sendWindowEvent(kEventOpenWindow);
}
void LM::m_moveToBack(int nargs) {
g_lingo->printSTUBWithArglist("m_moveToBack", nargs);
g_lingo->dropStack(nargs);
}
void LM::m_moveToFront(int nargs) {
Window *me = static_cast<Window *>(g_lingo->_state->me.u.obj);
me->ensureMovieIsLoaded();
bool wasActive = (g_director->_wm->getActiveWindow() == me->getId());
g_director->_wm->setActiveWindow(me->getId());
if (!wasActive)
me->sendWindowEvent(kEventOpenWindow);
}
// Actor
/*
collectChangeRects
getAProp
hitTest
ilk
mouseDown
mouseHitTest
mouseTrack
mouseUp
setAProp
stepFrame
updateRect
*/
} // End of namespace Director

View File

@@ -0,0 +1,283 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_OBJECT_H
#define DIRECTOR_LINGO_OBJECT_H
#include "director/lingo/lingo.h"
namespace Director {
struct MethodProto {
const char *name;
void (*func)(int);
int minArgs; // -1 -- arglist
int maxArgs;
int version;
};
struct XlibFileDesc {
const char *name; // Base file name for the Xlib file in the original
const char *gameId; // GameId or nullptr if applicable to all
};
class AbstractObject {
public:
virtual ~AbstractObject() {};
virtual Common::String getName() const = 0;
virtual ObjectType getObjType() const = 0;
virtual bool isDisposed() const = 0;
virtual int *getRefCount() const = 0;
virtual void incRefCount() = 0;
virtual void decRefCount() = 0;
virtual int getInheritanceLevel() const = 0;
virtual void setName(const Common::String &name) = 0;
virtual void dispose() = 0;
virtual Common::String asString() = 0;
virtual AbstractObject *clone() = 0;
virtual Symbol getMethod(const Common::String &methodName) = 0;
virtual bool hasProp(const Common::String &propName) = 0;
virtual Datum getProp(const Common::String &propName) = 0;
virtual Common::String getPropAt(uint32 index) = 0;
virtual uint32 getPropCount() = 0;
virtual void setProp(const Common::String &propName, const Datum &value, bool force = false) = 0;
virtual bool hasField(int field) = 0;
virtual Datum getField(int field) = 0;
virtual void setField(int field, const Datum &value) = 0;
};
template <typename Derived>
class Object : public AbstractObject {
public:
int *_refCount;
protected:
Object(Common::String objName) {
_name = objName;
_objType = kNoneObj;
_disposed = false;
_inheritanceLevel = 1;
_refCount = new int;
*_refCount = 0;
};
Object(const Object &obj) {
_name = obj._name;
_objType = obj._objType;
_disposed = obj._disposed;
_inheritanceLevel = obj._inheritanceLevel + 1;
_refCount = new int;
*_refCount = 0;
};
public:
static void initMethods(const MethodProto protos[]) {
if (_methods) {
warning("Object::initMethods: Methods already initialized");
return;
}
_methods = new SymbolHash;
for (const MethodProto *mtd = protos; mtd->name; mtd++) {
if (mtd->version > g_lingo->_vm->getVersion())
continue;
Symbol sym;
sym.name = new Common::String(mtd->name);
sym.type = HBLTIN;
sym.nargs = mtd->minArgs;
sym.maxArgs = mtd->maxArgs;
sym.u.bltin = mtd->func;
(*_methods)[mtd->name] = sym;
}
}
static void cleanupMethods() {
delete _methods;
_methods = nullptr;
}
virtual ~Object() {
delete _refCount;
};
Common::String getName() const override { return _name; };
ObjectType getObjType() const override { return _objType; };
bool isDisposed() const override { return _disposed; };
int *getRefCount() const override { return _refCount; };
void incRefCount() override { *_refCount += 1; };
virtual void decRefCount() override {
*_refCount -= 1;
if (*_refCount <= 0)
delete this;
};
int getInheritanceLevel() const override { return _inheritanceLevel; };
void setName(const Common::String &name) override { _name = name; };
void dispose() override { _disposed = true; };
Common::String asString() override {
return Common::String::format("object: #%s %d %p", _name.c_str(), _inheritanceLevel, (void *)this);
};
AbstractObject *clone() override {
return new Derived(static_cast<Derived const &>(*this));
};
Symbol getMethod(const Common::String &methodName) override {
Symbol sym;
if (_disposed) {
warning("Method '%s' called on disposed object <%s>, returning VOID", methodName.c_str(), asString().c_str());
return sym;
}
Common::String methodId;
if ((_objType & (kFactoryObj | kXObj)) && methodName.hasPrefixIgnoreCase("m")) {
methodId = methodName.substr(1);
} else {
methodId = methodName;
}
if (_methods && _methods->contains(methodId)) {
sym = (*_methods)[methodId];
sym.target = this;
return sym;
}
if (g_lingo->_methods.contains(methodId) && (g_lingo->_methods[methodId].targetType & _objType)) {
sym = g_lingo->_methods[methodId];
sym.target = this;
return sym;
}
return sym;
};
bool hasProp(const Common::String &propName) override {
return false;
};
Datum getProp(const Common::String &propName) override {
return Datum();
};
Common::String getPropAt(uint32 index) override {
return Common::String();
};
uint32 getPropCount() override {
return 0;
};
void setProp(const Common::String &propName, const Datum &value, bool force = false) override {
return;
};
bool hasField(int field) override {
return false;
};
Datum getField(int field) override {
return Datum();
};
void setField(int field, const Datum &value) override {
return;
};
protected:
static SymbolHash *_methods;
Common::String _name;
ObjectType _objType;
bool _disposed;
int _inheritanceLevel; // 1 for original object
};
template<typename Derived>
SymbolHash *Object<Derived>::_methods = nullptr;
class ScriptContext : public Object<ScriptContext> {
public:
ScriptType _scriptType;
int _id;
int _scriptId;
uint16 _parentNumber;
uint16 _castLibHint;
Common::Array<Common::String> _functionNames; // used by cb_localcall
Common::HashMap<Common::String, Common::Array<uint32>> _functionByteOffsets;
SymbolHash _functionHandlers;
Common::HashMap<uint32, Symbol> _eventHandlers;
Common::Array<Datum> _constants;
Common::HashMap<uint32, Datum> _objArray;
MethodHash _methodNames;
Common::SharedPtr<Node> _assemblyAST; // Optionally contains AST when we compile Lingo
private:
DatumHash _properties;
Common::Array<Common::String> _propertyNames;
bool _onlyInLctxContexts = false;
public:
ScriptContext(Common::String name, ScriptType type = kNoneScript, int id = 0, uint16 castLibHint = 0, uint16 parentNumber = 0, int scriptId = 0);
ScriptContext(const ScriptContext &sc);
~ScriptContext() override;
bool isFactory() const { return _objType == kFactoryObj; };
void setFactory(bool flag) { _objType = flag ? kFactoryObj : kScriptObj; }
void setOnlyInLctxContexts() { _onlyInLctxContexts = true; }
bool getOnlyInLctxContexts() { return _onlyInLctxContexts; }
Common::String asString() override;
Symbol getMethod(const Common::String &methodName) override;
bool hasProp(const Common::String &propName) override;
Datum getProp(const Common::String &propName) override;
Common::String getPropAt(uint32 index) override;
uint32 getPropCount() override;
void setProp(const Common::String &propName, const Datum &value, bool force = false) override;
Symbol define(const Common::String &name, ScriptData *code, Common::Array<Common::String> *argNames, Common::Array<Common::String> *varNames);
Common::String formatFunctionList(const char *prefix);
};
namespace LM {
// predefined methods
void m_describe(int nargs);
void m_dispose(int nargs);
void m_get(int nargs);
void m_instanceRespondsTo(int nargs);
void m_messageList(int nargs);
void m_name(int nargs);
void m_new(int nargs);
void m_perform(int nargs);
void m_put(int nargs);
void m_respondsTo(int nargs);
// window
void m_close(int nargs);
void m_forget(int nargs);
void m_moveToBack(int nargs);
void m_moveToFront(int nargs);
void m_open(int nargs);
} // End of namespace LM
} // End of namespace Director
#endif

View File

@@ -0,0 +1,609 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/punycode.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
#include "director/lingo/lingo-codegen.h"
namespace Director {
using namespace Common;
struct ScriptPatch {
const char *gameId;
const char *extra;
Common::Platform platform; // Specify kPlatformUnknown for skipping platform check
const char *movie;
ScriptType type;
uint16 id;
uint16 castLib;
int linenum;
const char *orig;
const char *replace;
} const scriptPatches[] = {
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "WARLOCKSHIP:UpForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
2, "SS Warlock:DATA:WARLOCKSHIP:Up.GCGunner", ""},
{"warlock", nullptr, kPlatformMacintosh, "WARLOCKSHIP:UpForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
3, "Channels 17 to 18", ""},
{"warlock", nullptr, kPlatformMacintosh, "WARLOCKSHIP:UpForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
4, "Frames 150 to 160", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:HE.Aft", kScoreScript, 8, DEFAULT_CAST_LIB,
2, "SS Warlock:DATA:WARLOCKSHIP:HangStairsFore", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:HE.Aft", kScoreScript, 8, DEFAULT_CAST_LIB,
3, "Channels 4 to 5", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:HE.Aft", kScoreScript, 8, DEFAULT_CAST_LIB,
4, "Frames 20 to 20", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:ENG:D10", kScoreScript, 8, DEFAULT_CAST_LIB,
2, "SS Warlock:ENG.Fold:C9", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:ENG:D10", kScoreScript, 8, DEFAULT_CAST_LIB,
3, "Channels 19 to 20", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:ENG:D10", kScoreScript, 8, DEFAULT_CAST_LIB,
4, "Frames 165 to 180", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.c2", kScoreScript, 10, DEFAULT_CAST_LIB,
2, "Frames 150 to 160", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.ForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
2, "SS Warlock:DATA:WARLOCKSHIP:Up.GCGunner", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.ForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
3, "Channels 17 to 18", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.ForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
4, "Frames 150 to 160", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.B2", kScoreScript, 9, DEFAULT_CAST_LIB,
2, "SS Warlock:DATA:WARLOCKSHIP:Up.GCGunner", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.B2", kScoreScript, 9, DEFAULT_CAST_LIB,
3, "Channels 17 to 18", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:Up.B2", kScoreScript, 9, DEFAULT_CAST_LIB,
4, "Frames 150 to 160", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:BELSHAZZAR:STELLA:ORIGIN", kScoreScript, 12, DEFAULT_CAST_LIB,
2, "Frames 1 to 1", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:BELSHAZZAR:STELLA:ORIGIN", kScoreScript, 13, DEFAULT_CAST_LIB,
2, "Frames 1 to 1", ""},
// Garbage at end of script
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:HangHallAft", kScoreScript, 7, DEFAULT_CAST_LIB,
2, "SS Warlock:DATA:WARLOCKSHIP:HangStairsFore", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:HangHallAft", kScoreScript, 7, DEFAULT_CAST_LIB,
3, "Channels 4 to 5", ""},
{"warlock", nullptr, kPlatformMacintosh, "DATA:WARLOCKSHIP:HangHallAft", kScoreScript, 7, DEFAULT_CAST_LIB,
4, "Frames 20 to 20", ""},
// Stray 'then' (obvious copy/paste error)
{"warlock", nullptr, kPlatformMacintosh, "DATA:K:KT:OutMarauderKT", kMovieScript, 14, DEFAULT_CAST_LIB,
23, "set Spacesuit = 0 then", "set Spacesuit = 0"},
// Missing '&'
{"warlock", nullptr, kPlatformMacintosh, "DATA:NAV:Shared Cast", kMovieScript, 510, DEFAULT_CAST_LIB,
19, "alert \"Failed Save.\" & return & \"Error message number: \" string ( filer )",
"alert \"Failed Save.\" & return & \"Error message number: \" & string ( filer )"},
// Garbage at end of script
{"warlock", "v1.1.3 MPC", kPlatformWindows, "WRLCKSHP:UpForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
2, "SS Warlock:DATA:WARLOCKSHIP:Up.GCGunner", ""},
{"warlock", "v1.1.3 MPC", kPlatformWindows, "WRLCKSHP:UpForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
3, "Channels 17 to 18", ""},
{"warlock", "v1.1.3 MPC", kPlatformWindows, "WRLCKSHP:UpForeECall", kScoreScript, 12, DEFAULT_CAST_LIB,
4, "Frames 150 to 160", ""},
// Missing '&'
{"warlock", nullptr, kPlatformUnknown, "NAV:Shared Cast", kMovieScript, 510, DEFAULT_CAST_LIB,
23, "alert \"Failed Save.\" & return & \"Error message number: \" string ( filer )",
"alert \"Failed Save.\" & return & \"Error message number: \" & string ( filer )"},
// Non-existent menu cast reference
{"warlock", nullptr, kPlatformWindows, "STARBIRD:ABOUT", kScoreScript, 4, DEFAULT_CAST_LIB,
1, "installmenu A13", ""},
// Typo
{"rodneyfs", nullptr, kPlatformMacintosh, "Shared Cast", kMovieScript, 496, DEFAULT_CAST_LIB,
7, "if the soundLevel <> 7 then set the the soundLevel to 7", "if the soundLevel <> 7 then set the soundLevel to 7"},
// Patching dead loop which was fixed in v2
{"lzone", "", kPlatformMacintosh, "DATA:R-A:Ami-00", kScoreScript, 3, DEFAULT_CAST_LIB,
2, "continue", "go \"OUT\""},
// Garbage at end of statements
{"lzone", "", kPlatformMacintosh, "DATA:R-E:ZD2-LAS", kScoreScript, 7, DEFAULT_CAST_LIB,
4, "go to the frame 0", "go to the frame"},
{"lzone", "", kPlatformMacintosh, "DATA:R-E:zd1-con1", kScoreScript, 27, DEFAULT_CAST_LIB,
1, "go to the frame 0", "go to the frame"},
{"lzone", "", kPlatformMacintosh, "DATA:R-E:zd1-con1", kScoreScript, 30, DEFAULT_CAST_LIB,
4, "go the frame 0", "go to the frame"},
{"lzone", "", kPlatformMacintosh, "DATA:R-G:st-c", kScoreScript, 14, DEFAULT_CAST_LIB,
1, "go to the frame 0", "go to the frame"},
{"lzone", "", kPlatformMacintosh, "DATA:R-G:st-d.mo", kScoreScript, 4, DEFAULT_CAST_LIB,
1, "go to the frame 0", "go to the frame"},
{"lzone", "", kPlatformMacintosh, "DATA:R-F:ARCH-U.D-1", kScoreScript, 8, DEFAULT_CAST_LIB,
1, "GO \"SPACE\" OF MOVIE \"L-ZONE:DATA:R-G:ST-A2\",\"242,197\"",
"GO \"SPACE\" OF MOVIE \"L-ZONE:DATA:R-G:ST-A2\""},
{"lingoexpo", "", kPlatformMacintosh, "Lingo Expo:Navigator", kMovieScript, 9, DEFAULT_CAST_LIB,
97, " append(codeExampleList,\"6,301,302,303,304,305,306\") - KIOSK SCRIPTS",
" append(codeExampleList,\"6,301,302,303,304,305,306\")"},
{"jman", "", kPlatformWindows, "mmm:Mars Space Game 05", kMovieScript, 10, DEFAULT_CAST_LIB,
68, "set DamageParameter = (gProcessorSpeed/2) + 7)",
"set DamageParameter = (gProcessorSpeed/2) + 7"},
{"jman", "", kPlatformWindows, "MMM:Shared Cast B&W", kMovieScript, 323, DEFAULT_CAST_LIB,
187, "set the trails of sprite 19 to 0", "set the locH of sprite 19 to 408"},
{"jman", "", kPlatformWindows, "MMM:Shared Cast B&W", kMovieScript, 323, DEFAULT_CAST_LIB,
188, "set the locH of sprite 19 to 408", "set the locV of sprite 19 to 168"},
{"jman", "", kPlatformWindows, "MMM:Shared Cast B&W", kMovieScript, 323, DEFAULT_CAST_LIB,
189, "set the locV of sprite 19 to 168", "set the text of field \"Description\" = description"},
{"jman", "", kPlatformWindows, "MMM:Shared Cast B&W", kMovieScript, 323, DEFAULT_CAST_LIB,
190, "set the text of field \"Description\" = description", "set the castnum of sprite 19 to the number of cast \"Description\""},
{"jman", "", kPlatformWindows, "MMM:Shared Cast B&W", kMovieScript, 323, DEFAULT_CAST_LIB,
191, "set the castnum of sprite 19 to the number of cast \"Description\"", "updateStage"},
{"jman", "", kPlatformWindows, "MMM:Shared Cast B&W", kMovieScript, 323, DEFAULT_CAST_LIB,
192, "updateStage", "set the trails of sprite 19 to 0"},
{"snh", "Hybrid release", kPlatformWindows, "SNHstart", kMovieScript, 0, DEFAULT_CAST_LIB,
3, "changedrive", ""}, // HACK: This macro inserts \x01 after the first character in myCD/myHD
{"snh", "Hybrid release", kPlatformWindows, "SNHstart", kMovieScript, 0, DEFAULT_CAST_LIB,
6, "set mytest2 = FileIO(mnew, \"read\" mymovie)", "set mytest2 = FileIO(mnew, \"read\", mymovie)"},
{"snh", "Hybrid release", kPlatformWindows, "SNHstart", kMovieScript, 0, DEFAULT_CAST_LIB,
14, "set mytest3 = FileIO(mnew, \"read\" mymovie)", "set mytest3 = FileIO(mnew, \"read\", mymovie)"},
// Ambiguous syntax that's parsed differently between D3 and later versions
{"henachoco03", "", kPlatformMacintosh, "xn--oj7cxalkre7cjz1d2agc0e8b1cm", kMovieScript, 0, DEFAULT_CAST_LIB,
183, "locaobject(mLHizikaraHand (rhenka + 1),dotti)", "locaobject(mLHizikaraHand,(rhenka + 1),dotti)"},
{"henachoco03", "", kPlatformMacintosh, "xn--oj7cxalkre7cjz1d2agc0e8b1cm", kMovieScript, 0, DEFAULT_CAST_LIB,
196, "locaobject(mRHizikaraHand (rhenka + 1),dotti)", "locaobject(mRHizikaraHand,(rhenka + 1),dotti)"},
// Same patch applied to the demos, with different line numbers
{"henachoco03", "Trial Version", kPlatformMacintosh, "ITA Choco", kMovieScript, 0, DEFAULT_CAST_LIB,
123, "locaobject(mLHizikaraHand (rhenka + 1),dotti)", "locaobject(mLHizikaraHand,(rhenka + 1),dotti)"},
{"henachoco03", "Trial Version", kPlatformMacintosh, "ITA Choco", kMovieScript, 0, DEFAULT_CAST_LIB,
136, "locaobject(mRHizikaraHand (rhenka + 1),dotti)", "locaobject(mRHizikaraHand,(rhenka + 1),dotti)"},
{"henachoco03", "Demo", kPlatformMacintosh, "Muzukashiihon", kMovieScript, 0, DEFAULT_CAST_LIB,
123, "locaobject(mLHizikaraHand (rhenka + 1),dotti)", "locaobject(mLHizikaraHand,(rhenka + 1),dotti)"},
{"henachoco03", "Demo", kPlatformMacintosh, "Muzukashiihon", kMovieScript, 0, DEFAULT_CAST_LIB,
136, "locaobject(mRHizikaraHand (rhenka + 1),dotti)", "locaobject(mRHizikaraHand,(rhenka + 1),dotti)"},
// The same ambiguous syntax as above, in a different disc
{"journey2source", "", kPlatformMacintosh, "StartJourney", kScoreScript, 2, DEFAULT_CAST_LIB,
2, "set DiskChk = FileIO(mnew,\"read\"¬\"The Source:Put Contents on Hard Drive:Journey to the Source:YZ.DATA\")", "set DiskChk = FileIO(mnew,\"read\"\"The Source:Put Contents on Hard Drive:Journey to the Source:YZ.DATA\")"},
// C.H.A.O.S
{"chaos", "", kPlatformWindows, "Intro", kCastScript, 10, DEFAULT_CAST_LIB,
9, "rHyperPACo \"blank\", 498, 350 gGenPathWay", "rHyperPACo \"blank\", 498, 350, gGenPathWay"},
{"smile", "v1.1", kPlatformMacintosh, "SMILE! The Splattering", kScoreScript, 24, DEFAULT_CAST_LIB,
1, "go to frame \"Info b\"If you have not paid ", "go to frame \"Info b\""},
// Hack to fix the undefined sprite collision behaviour relied on by the boar hunt
{"wrath", "", kPlatformWindows, "57AM1", kMovieScript, 1, DEFAULT_CAST_LIB,
385, "(StartV57a-6) <= YesV57a", " if sprite 5 intersects 3 and StartV57a <= YesV57a + 16 then"},
{"wrath", "", kPlatformMacintosh, "Wrath:57AM1", kMovieScript, 1, DEFAULT_CAST_LIB,
382, "(StartV57a-6) <= YesV57a", " if sprite 5 intersects 3 and StartV57a <= YesV57a + 16 then"},
{"amandastories", "", kPlatformWindows, "Shared Cast", kMovieScript, 512, DEFAULT_CAST_LIB,
55, " set mytest1 = FileIO(mnew, \"read\" mymovie)", " set mytest1 = FileIO(mnew, \"read\", mymovie)"},
{"amandastories", "", kPlatformWindows, "Shared Cast", kMovieScript, 512, DEFAULT_CAST_LIB,
63, " set mytest2 = FileIO(mnew, \"read\" mymovie)", " set mytest2 = FileIO(mnew, \"read\", mymovie)"},
{"amandastories", "", kPlatformWindows, "Shared Cast", kMovieScript, 512, DEFAULT_CAST_LIB,
70, " set mytest3 = FileIO(mnew, \"read\" mymovie)", " set mytest3 = FileIO(mnew, \"read\", mymovie)"},
{"amandastories", "", kPlatformWindows, "ASstart", kMovieScript, 0, DEFAULT_CAST_LIB,
5, " set mytest = FileIO(mnew, \"read\" mymovie)", " set mytest = FileIO(mnew, \"read\", mymovie)"},
{"amandastories", "", kPlatformWindows, "ASstart", kMovieScript, 0, DEFAULT_CAST_LIB,
11, " set mytest2 = FileIO(mnew, \"read\" mymovie)", " set mytest2 = FileIO(mnew, \"read\", mymovie)"},
{"amandastories", "", kPlatformWindows, "ASstart", kMovieScript, 0, DEFAULT_CAST_LIB,
19, " set mytest3 = FileIO(mnew, \"read\" mymovie)", " set mytest3 = FileIO(mnew, \"read\", mymovie)"},
{"erikotamuraoz", "Demo", kPlatformMacintosh, "Shared Cast", kMovieScript, 391, DEFAULT_CAST_LIB,
21, "", "end repeat"},
{"cts", "Metric", kPlatformMacintosh, "CTS", kMovieScript, 0, DEFAULT_CAST_LIB,
307, " alert(\"Sorry. No keyword was entered for this recipe.)", " alert(\"Sorry. No keyword was entered for this recipe.\")"},
{"cts", "Imperial", kPlatformMacintosh, "CTS", kMovieScript, 0, DEFAULT_CAST_LIB,
307, " alert(\"Sorry. No keyword was entered for this recipe.)", " alert(\"Sorry. No keyword was entered for this recipe.\")"},
// garbage script
{"refixion2", "", kPlatformMacintosh, "data:Movie:ROgo", kScoreScript, 3, DEFAULT_CAST_LIB,
1, "Are you sure to cut off KANJI Talk", ""},
{nullptr, nullptr, kPlatformUnknown, nullptr, kNoneScript, 0, 0, 0, nullptr, nullptr}
};
/*
* Cosmology of Kyoto has a text entry system, however for the English version
* at least you are very unlikely to guess the correct sequence of letters that
* constitute a valid answer. This is an attempt to make things fairer by removing
* the need for precise whitespace and punctuation. As a fallback, "yes" should
* always mean a yes response, and "no" should always mean a no response.
*/
const char *const kyotoTextEntryFix = " \
on scrubInput inputString \r\
set result = \"\" \r\
repeat with x = 1 to the number of chars in inputString \r\
if chars(inputString, x, x) = \" \" then continue \r\
else if chars(inputString, x, x) = \".\" then continue \r\
else if chars(inputString, x, x) = \"!\" then continue \r\
else if chars(inputString, x, x) = \"?\" then continue \r\
else if chars(inputString, x, x) = \"\" then continue \r\
else \r\
set result = result & char x of inputString \r\
end if \r\
end repeat \r\
return result \r\
end \r\
\r\
on checkkaiwa kaiwatrue, kaiwafalse \r\
global myparadata \r\
if (keyCode() <> 36) and (keyCode() <> 76) then \r\
exit \r\
end if \r\
put \"Original YES options: \" & kaiwatrue \r\
put \"Original NO options: \" & kaiwafalse \r\
-- yes and no should always give consistent results \r\
if kaiwaans = \"yes\" then \r\
return \"YES\" \r\
else if kaiwaans = \"no\" then \r\
return \"NO\" \r\
end if \r\
-- pre-scrub all input and choices to remove effect of whitespace/punctuation \r\
set kaiwaans = scrubInput(field \"KaiwaWindow\") \r\
set kaiwatrue = scrubInput(kaiwatrue) \r\
set kaiwafalse = scrubInput(kaiwafalse) \r\
repeat with y = 1 to the number of items in kaiwatrue \r\
if item y of kaiwatrue starts kaiwaans then \r\
when keyDown then CheckQuit \r\
put EMPTY into field \"KaiwaWindow\" \r\
return \"YES\" \r\
end if \r\
end repeat \r\
repeat with n = 1 to the number of items in kaiwafalse \r\
if item n of kaiwafalse starts kaiwaans then \r\
when keyDown then CheckQuit \r\
put EMPTY into field \"KaiwaWindow\" \r\
return \"NO\" \r\
end if \r\
end repeat \r\
set kaiwafool to scrubInput(\"あほんだら,ばか,うんこ,しっこ,しね,死ね,うるさい,うるせえ,\" & \"fool,simpleton,stupid person,kill,Shut up!,Get out of my hair!\") \r\
repeat with f = 1 to the number of items in kaiwafool \r\
if item f of kaiwafool starts kaiwaans then \r\
myparadata(maddparadata, 2, 1) \r\
when keyDown then CheckQuit \r\
put EMPTY into field \"KaiwaWindow\" \r\
return \"error\" \r\
end if \r\
end repeat \r\
when keyDown then CheckQuit \r\
put EMPTY into field \"KaiwaWindow\" \r\
return \"error\" \r\
end \r\
";
/*
* Virtual Nightclub will try and list all the files from all 26 drive letters
* to determine which has the CD. This works, but takes forever.
*/
const char *const vncSkipDetection = " \
global cdDriveLetter, gMultiDisk \r\
on findVNCVolume \r\
set cdDriveLetter to \"D\" \r\
set gMultiDisk to 1 \r\
return 1 \r\
end \r\
";
/*
* Virtual Nightclub has a number of cheat codes for debugging.
* These are normally enabled by pressing Option + 0, however the
* released game has this code stubbed out with a return.
*/
const char *const vncEnableCheats = " \
on togCh\r\
if getFlag(#cheats) then\r\
setFlag(#cheats, 0)\r\
setMode(0) -- disable debug logging\r\
set the foreColor of field \"viewName_cast\" to 255\r\
alert(\"VNC Cheats off\")\r\
else\r\
if platform() < 256 then\r\
set the textFont of field \"viewName_cast\" to \"Monaco\"\r\
end if\r\
set the foreColor of field \"viewName_cast\" to 172\r\
set the textSize of field \"viewName_cast\" to 9\r\
setFlag(#cheats)\r\
setMode(10) -- enable debug logging\r\
alert(\"VNC Cheats on\")\r\
end if\r\
end\r\
";
/*
* AMBER: Journeys Beyond has a check to ensure that the CD and hard disk data are on
* different drive letters. ScummVM will pretend that every drive letter contains the
* game contents, so we need to hotpatch the CD detection routine to return D:.
*/
const char *const amberDriveDetectionFix = " \
on GetCDLetter tagFile, discNumber\r\
return \"D:\"\r\
end \r\
";
/* Frankenstein: Through The Eyes Of The Monster uses a projector FRANKIE.EXE, which calls an
* identically-named submovie FRANKIE.DIR. For now we can work around this mess by referring to
* the full "path" of the embedded submovie so path detection doesn't collide with FRANKIE.EXE.
*/
const char *const frankensteinSwapFix = " \
on exitFrame \r\
go(1, \"FRANKIE\\FRANKIE.DIR\")\r\
end \r\
";
/* GADGET: Past As Future was released on 4 CDs, and detects which CD is present by reading
* a file "diskid.txt". Replace this with the honour system so we can support merging the
* contents of all 4 discs.
*/
const char *const gadgetPafDetectionFixAlert = " \
on exitFrame \r\
end \r\
";
const char *const gadgetPafDetectionFix12 = " \
on exitFrame \r\
go(\"start-ok\")\r\
end \r\
";
const char *const gadgetPafDetectionFix13 = " \
on exitFrame \r\
go(\"load-ok\")\r\
end \r\
";
const char *const gadgetPafDetectionFix4 = " \
on exitFrame \r\
go(\"eject1-ok\")\r\
end \r\
";
const char *const gadgetPafDetectionFix6 = " \
on exitFrame \r\
go(\"eject3-ok\")\r\
end \r\
";
const char *const gadgetPafDetectionFix9 = " \
on exitFrame \r\
go(\"eject2-ok\")\r\
end \r\
";
/*
* Pink Gear Collection has a check to ensure that the CD and hard disk data are on
* different drive letters by checking if "PINKPINK.TXT" is the first file in the
* "PG_WORLD\PINKCD" folder. Later, it iterates over every drive letter to find the CD
* using the same method. Removing this check as ScummVM will pretend that every drive
* letter contains the game contents.
*/
const char *const pinkGearDriveDetectionFix1 = " \
on startMovie\r\
global oricolor, projname, mtype\r\
cursor(200)\r\
set oricolor to the colorDepth\r\
set projname to the pathName\r\
if oricolor <> 8 then\r\
sound fadeIn 1, 1 * 60\r\
puppetSound(\"BAMEN11k\")\r\
go(\"noH2\")\r\
else\r\
set mtype to 2\r\
go(\"01\")\r\
end if\r\
";
const char *const pinkGearDriveDetectionFix2 = " \
on exitFrame\r\
go(1, \"C:\\PG_WORLD\\A_IN01\")\r\
";
/*
* Mission Code: Millennium has some drive detection code which prevents the game from loading
* if it detects DESTINA.MLD is present "on the hard disk". Provide the same code without that check.
*/
const char *const mcmillenniumDriveDetectionFix = "\
on initPaths\r\
global PD, theCDPath, theHDPath, theVCAudioPath, theNotePath, proxPath\r\
if the machineType = 256 then\r\
set PD to \"\\\"\r\
else\r\
set PD to \":\"\r\
end if\r\
if the machineType = 256 then\r\
set theCDPath to getAt(the searchPaths, 2)\r\
else\r\
if checkFileExists(the pathName & \"DESTINA.MLD\") = 1 then\r\
set theCDPath to the pathName\r\
else\
set theCDPath to \"Millennium:\"\r\
end if\r\
end if\r\
set theHDPath to the pathName\r\
set theVCAudioPath to theCDPath & \"AUDIO\" & PD & \"VIDCOM\" & PD\r\
set theNotePath to the pathName\r\
set proxPath to theCDPath & \"prox\" & PD\r\
end\r\
";
/*
* Mission Code: Millennium has a bizarre method of checking the dimensions of the screen by
* measuring the stage position of a 512x384 movie and seeing if it matches 640x480.
* Even when forcing desktop mode this doesn't match up exactly, so patch it out.
*/
const char *const mcmillenniumResDetectionFix = "\
on getRes\r\
end\r\
";
/*
* GORD@K has a complicated CD detection method which includes writing a temp file to the CD
* drive. Since this always works, we have to stub out the entire method.
*/
const char *const gordakDetectionFix = "\
on checkFiles\r\
go to movie \"gordak\\intro.dxr\"\r\
end\r\
";
struct ScriptHandlerPatch {
const char *gameId;
const char *extra;
Common::Platform platform; // Specify kPlatformUnknown for skipping platform check
const char *movie;
ScriptType type;
uint16 id;
uint16 castLib;
const char *const *handlerBody;
} const scriptHandlerPatches[] = {
{"kyoto", nullptr, kPlatformWindows, "ck_data\\dd_dairi\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\findfldr\\shared.dxr", kMovieScript, 802, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\ichi\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\jigoku\\shared.dxr", kMovieScript, 840, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\kusamura\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\map01\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\map02\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\map03\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\map04\\shared.dxr", kMovieScript, 906, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\opening\\shared.dxr", kMovieScript, 802, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\rajoumon\\shared.dxr", kMovieScript, 840, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"kyoto", nullptr, kPlatformWindows, "ck_data\\rokudou\\shared.dxr", kMovieScript, 846, DEFAULT_CAST_LIB, &kyotoTextEntryFix},
{"vnc", nullptr, kPlatformWindows, "VNC\\VNC.EXE", kMovieScript, 57, DEFAULT_CAST_LIB, &vncSkipDetection},
{"vnc", nullptr, kPlatformWindows, "VNC2\\SHARED.DXR", kMovieScript, 1248, DEFAULT_CAST_LIB, &vncEnableCheats},
{"amber", nullptr, kPlatformWindows, "AMBER_F\\AMBER_JB.EXE", kMovieScript, 7, DEFAULT_CAST_LIB, &amberDriveDetectionFix},
{"frankenstein", nullptr, kPlatformWindows, "FRANKIE.EXE", kScoreScript, 21, DEFAULT_CAST_LIB, &frankensteinSwapFix},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 2, DEFAULT_CAST_LIB, &gadgetPafDetectionFixAlert},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 8, DEFAULT_CAST_LIB, &gadgetPafDetectionFixAlert},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 10, DEFAULT_CAST_LIB, &gadgetPafDetectionFixAlert},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 11, DEFAULT_CAST_LIB, &gadgetPafDetectionFixAlert},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 18, DEFAULT_CAST_LIB, &gadgetPafDetectionFixAlert},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 12, DEFAULT_CAST_LIB, &gadgetPafDetectionFix12},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 13, DEFAULT_CAST_LIB, &gadgetPafDetectionFix13},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 4, DEFAULT_CAST_LIB, &gadgetPafDetectionFix4},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\DISKCNG.DIR", kScoreScript, 6, DEFAULT_CAST_LIB, &gadgetPafDetectionFix6},
{"gadgetpaf", nullptr, kPlatformWindows, "GADGET\\GADGET.EXE", kScoreScript, 9, DEFAULT_CAST_LIB, &gadgetPafDetectionFix9},
{"pinkgear", nullptr, kPlatformWindows, "GOTOPINK.EXE", kMovieScript, 4, DEFAULT_CAST_LIB, &pinkGearDriveDetectionFix1},
{"pinkgear", nullptr, kPlatformWindows, "GOTOPINK.EXE", kScoreScript, 6, DEFAULT_CAST_LIB, &pinkGearDriveDetectionFix2},
{"mcmillennium", nullptr, kPlatformWindows, "PC\\MILL.EXE", kMovieScript, 15, DEFAULT_CAST_LIB, &mcmillenniumResDetectionFix},
{"mcmillennium", nullptr, kPlatformMacintosh, "Mission Code Millennium:Mission Code Millennium", kMovieScript, 15, DEFAULT_CAST_LIB, &mcmillenniumResDetectionFix},
{"mcmillennium", nullptr, kPlatformWindows, "PC\\SHARED.DXR", kMovieScript, 1013, DEFAULT_CAST_LIB, &mcmillenniumDriveDetectionFix},
{"mcmillennium", nullptr, kPlatformMacintosh, "Mission Code Millennium:SHARED.Dxr", kMovieScript, 1013, DEFAULT_CAST_LIB, &mcmillenniumDriveDetectionFix},
{"gordak", nullptr, kPlatformWindows, "GORDAKCD.EXE", kMovieScript, 2, DEFAULT_CAST_LIB, &gordakDetectionFix},
{nullptr, nullptr, kPlatformUnknown, nullptr, kNoneScript, 0, 0, nullptr},
};
void LingoArchive::patchScriptHandler(ScriptType type, CastMemberID id) {
const ScriptHandlerPatch *patch = scriptHandlerPatches;
Common::String movie = g_director->getCurrentPath() + cast->getMacName();
// So far, we have not many patches, so do linear lookup
while (patch->gameId) {
// First, we do cheap comparisons
if (patch->type != type || patch->id != id.member || patch->castLib != id.castLib ||
(patch->platform != kPlatformUnknown && patch->platform != g_director->getPlatform())) {
patch++;
continue;
}
// Now expensive ones
U32String moviename = punycode_decode(patch->movie);
if (movie.compareToIgnoreCase(moviename) || strcmp(patch->gameId, g_director->getGameId())
|| (patch->extra && strcmp(patch->extra, g_director->getExtra()))) {
patch++;
continue;
}
patchCode(Common::U32String(*patch->handlerBody), patch->type, patch->id);
patch++;
}
}
Common::U32String LingoCompiler::patchLingoCode(const Common::U32String &line, LingoArchive *archive, ScriptType type, CastMemberID id, int linenum) {
if (!archive)
return line;
const ScriptPatch *patch = scriptPatches;
Common::String movie = g_director->getCurrentPath() + archive->cast->getMacName();
// So far, we have not many patches, so do linear lookup
while (patch->gameId) {
// First, we do cheap comparisons
if (patch->type != type || patch->id != id.member || patch->castLib != id.castLib || patch->linenum != linenum ||
(patch->platform != kPlatformUnknown && patch->platform != g_director->getPlatform())) {
patch++;
continue;
}
// Now expensive ones
U32String moviename = punycode_decode(patch->movie);
if (movie.compareToIgnoreCase(moviename) || strcmp(patch->gameId, g_director->getGameId())
|| (patch->extra && strcmp(patch->extra, g_director->getExtra()))) {
patch++;
continue;
}
// Now do a safeguard
if (!line.contains(Common::U32String(patch->orig)) && line.encode().c_str() != Common::U32String()) {
warning("Lingo::patchLingoCode(): Unmatched patch for '%s', '%s' %s:%s @ %d. Expecting '%s' but got '%s'",
patch->gameId, patch->movie, scriptType2str(type), id.asString().c_str(), linenum,
patch->orig, line.encode().c_str());
return line;
}
// Now everything matched
warning("Lingo::patchLingoCode(): Applied a patch for '%s', '%s' %s:%s @ %d. \"%s\" -> \"%s\"",
patch->gameId, patch->movie, scriptType2str(type), id.asString().c_str(), linenum,
patch->orig, patch->replace);
return Common::U32String(patch->replace);
}
return line;
}
} // End of namespace Director

View File

@@ -0,0 +1,297 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/movie.h"
#include "director/lingo/lingo-codegen.h"
#include "director/types.h"
namespace Director {
bool isspec(Common::u32char_type_t c) {
if (c > 127)
return false;
return strchr("-+*/%^:,()><&[]=", (char)c) != nullptr;
}
static Common::U32String nexttok(const Common::u32char_type_t *s, const Common::u32char_type_t **newP = nullptr) {
Common::U32String res;
// Scan first non-whitespace
while (*s && (*s == ' ' || *s == '\t' || *s == CONTINUATION)) // If we see a whitespace
s++;
if (*s == '"') { // If it is a string then scan till end quote
res += *s++;
while (*s && *s != '"')
res += *s++;
if (*s == '"')
res += *s++;
} else if (Common::isAlnum(*s) || *s == '#' || *s == '.') {
// Now copy everything till whitespace
while (*s && (Common::isAlnum(*s) || *s == '.' || *s == '#' || *s == '_'))
res += *s++;
} else {
while (*s && isspec(*s))
res += *s++;
}
if (newP)
*newP = s;
return res;
}
Common::U32String LingoCompiler::codePreprocessor(const Common::U32String &code, LingoArchive *archive, ScriptType type, CastMemberID id, uint32 flags) {
const Common::u32char_type_t *s = code.c_str();
Common::U32String res;
if (debugChannelSet(2, kDebugPreprocess)) {
Common::String movie = g_director->getCurrentPath();
if (archive)
movie += archive->cast->getMacName();
debugC(2, kDebugPreprocess, "LingoCompiler::codePreprocessor: \"%s\", %s, %d, %d", movie.c_str(), scriptType2str(type), id.member, id.castLib);
}
// We start from processing the continuation symbols
// (The continuation symbol is \xC2 in Mac Roman, \xAC in Unicode.)
// \xAC\n -> \xAC
// This will greatly simplify newline processing, still leaving
// the line number tracking intact
while (*s) {
if (*s == CONTINUATION) {
res += *s++;
if (!*s) // Who knows, maybe it is the last symbol in the script
break;
s++;
continue;
} else if (*s == 0xFF82) { // Misparsed Japanese continuation
if (!*(s+1)) { // EOS - write as is and finish up
res += *s++;
break;
}
// Next character isn't a newline; write as is and keep
// going.
if (*(s+1) != 13) {
res += *s++;
continue;
}
s++;
// This is a bit of a hack; in MacJapanese the codepoint at
// C2 is the half-width katakana "tsu", so ScummVM is
// getting confused about what's here in the script after
// translating from MacJapanese to Unicode.
// Just swap the character out for the right Unicode character here.
// This can be removed if Lingo parsing is reworked to act
// on the original raw bytes instead of a Unicode translation.
res += CONTINUATION;
if (!*s)
break;
s++;
continue;
}
res += *s++;
}
Common::U32String tmp(res);
res.clear();
s = tmp.c_str();
// Strip comments
bool inString = false;
while (*s) {
if (*s == '"')
inString = !inString;
if (*s == '\r' || *s == '\n') // Lingo does not allow multiline strings
inString = false;
if (!inString && *s == '-' && *(s + 1) == '-') { // At the end of the line we will have \0
while (*s && *s != '\r' && *s != '\n')
s++;
}
if (*s == '\r')
res += '\n';
else if (*s)
res += *s;
if (*s)
s++;
}
tmp = res;
res.clear();
// Strip trailing whitespaces
s = tmp.c_str();
while (*s) {
if (*s == ' ' || *s == '\t' || *s == CONTINUATION) { // If we see a whitespace
const Common::u32char_type_t *ps = s; // Remember where we saw it
while (*ps == ' ' || *ps == '\t' || *ps == CONTINUATION) // Scan until end of whitespaces
ps++;
if (*ps) { // Not end of the string
if (*ps == '\n') { // If it is newline, then we continue from it
s = ps;
} else { // It is not a newline
while (s != ps) { // Add all whitespaces
res += *s;
s++;
}
}
}
}
if (*s)
res += *s;
s++;
}
if (flags & kLPPSimple)
return res;
tmp = res;
s = tmp.c_str();
res.clear();
Common::U32String line, tok, res1;
int linenumber = 1;
bool defFound = false;
const Common::U32String macro("macro"), factory("factory"), on("on"), global("global"), property("property"),
mci("mci");
while (*s) {
line.clear();
res1.clear();
// Get next line
int continuationCount = 0;
while (*s && *s != '\n') { // If we see a whitespace
res1 += *s;
line += tolower(*s++);
if (*s == CONTINUATION) {
linenumber++;
continuationCount++;
}
}
debugC(2, kDebugPreprocess, "line %d: '%s'", linenumber, line.encode().c_str());
if (!defFound && (type == kMovieScript || type == kCastScript) && (g_director->getVersion() < 400 || g_director->getCurrentMovie()->_allowOutdatedLingo)) {
tok = nexttok(line.c_str());
if (tok.equals(macro) || tok.equals(factory)) {
defFound = true;
} else if (!(flags & kLPPForceD2)) {
if (tok.equals(on) || tok.equals(global) || tok.equals(property)) {
defFound = true;
}
}
if (!defFound) {
debugC(2, kDebugPreprocess, "skipping line before first definition");
for (int i = 0; i < continuationCount; i++) {
res += CONTINUATION;
}
linenumber++;
if (*s) // copy newline symbol
res += *s++;
continue;
}
}
// In MultiMedia Movie format, .MMM files used by Microsoft
// 'mci' keyword is followed by the unquoted commands, e.g.
// mci close all
// mci play wave to 15228 hold
//
// Since Director requires them in a single thing, we add
// quotes around
const Common::u32char_type_t *contLine;
tok = nexttok(line.c_str(), &contLine);
if (tok.equals(mci) && *contLine != 0 && !Common::U32String(contLine).contains('\"')) {
// Scan first non-whitespace
while (*contLine && (*contLine == ' ' || *contLine == '\t' || *contLine == CONTINUATION)) // If we see a whitespace
contLine++;
res1 = Common::U32String::format("%S \"%S\"", tok.c_str(), contLine);
debugC(2, kDebugPreprocess, "wrapped mci command into quotes");
}
res1 = patchLingoCode(res1, archive, type, id, linenumber);
res += res1;
linenumber++; // We do it here because of 'continue' statements
if (*s) // copy newline symbol
res += *s++;
}
// Make the parser happier when there is no newline at the end
res += '\n';
debugC(2, kDebugPreprocess, "#############\n%s\n#############", res.encode().c_str());
return res;
}
MethodHash LingoCompiler::prescanMethods(const Common::U32String &code) {
const Common::u32char_type_t *s = code.c_str();
Common::U32String line, tok;
MethodHash res;
const Common::U32String macro("macro"), on("on"), method("method");
while (*s) {
line.clear();
// Get next line
while (*s && *s != '\n')
line += tolower(*s++);
const Common::u32char_type_t *contLine;
tok = nexttok(line.c_str(), &contLine);
if ((tok.equals(macro) || tok.equals(on) || tok.equals(method)) && *contLine != 0) {
Common::U32String methodname = nexttok(contLine);
res[methodname] = true;
}
if (*s)
s++; // Newline symbol
}
return res;
}
} // End of namespace Director

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,343 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_LINGO_THE_H
#define DIRECTOR_LINGO_LINGO_THE_H
namespace Director {
enum TheEntityType {
kTheNOEntity = 0,
kTheObject = 1,
kTheActiveWindow,
kTheActorList,
kTheAlertHook,
kTheApplicationPath,
kTheBeepOn,
kTheButtonStyle,
kTheCast,
kTheCastLib,
kTheCastLibs,
kTheCastMembers,
kTheCenterStage,
kTheChars,
kTheCheckBoxAccess,
kTheCheckBoxType,
kTheChunk,
kTheClickLoc,
kTheClickOn,
kTheColorDepth,
kTheColorQD,
kTheCommandDown,
kTheControlDown,
kTheCpuHogTicks,
kTheCurrentSpriteNum,
kTheDate,
kTheDeskTopRectList,
kTheDigitalVideoTimeScale,
kTheDoubleClick,
kTheEmulateMultiButtonMouse,
kTheExitLock,
kTheField,
kTheFixStageSize,
kTheFloatPrecision,
kTheFrame,
kTheFrameLabel,
kTheFramePalette,
kTheFrameScript,
kTheFrameSound1,
kTheFrameSound2,
kTheFrameTempo,
kTheFrameTransition,
kTheFreeBlock,
kTheFreeBytes,
kTheFrontWindow,
kTheFullColorPermit,
kTheIdleHandlerPeriod,
kTheIdleLoadMode,
kTheIdleLoadPeriod,
kTheIdleLoadTag,
kTheIdleReadChunkSize,
kTheImageDirect,
kTheItemDelimiter,
kTheItems,
kTheKey,
kTheKeyCode,
kTheKeyDownScript,
kTheKeyPressed,
kTheKeyUpScript,
kTheLabelList,
kTheLastClick,
kTheLastEvent,
kTheLastFrame,
kTheLastKey,
kTheLastRoll,
kTheLines,
kTheMachineType,
kTheMaxInteger,
kTheMemorySize,
kTheMenu,
kTheMenuItem,
kTheMenuItems,
kTheMenus,
kTheMouseCast,
kTheMouseChar,
kTheMouseDown,
kTheMouseDownScript,
kTheMouseH,
kTheMouseItem,
kTheMouseLine,
kTheMouseMember,
kTheMouseUp,
kTheMouseUpScript,
kTheMouseV,
kTheMouseWord,
kTheMovie,
kTheMovieFileFreeSize,
kTheMovieFileSize,
kTheMovieName,
kTheMoviePath,
kTheMultiSound,
kTheNetThrottleTicks,
kTheOptionDown,
kTheOrganizationName,
kTheParamCount,
kThePathName,
kThePauseState,
kThePerFrameHook,
kThePi,
kThePlatform,
kThePreloadEventAbort,
kThePreLoadRAM,
kTheProductName,
kTheProductVersion,
kTheQuickTimePresent,
kTheRandomSeed,
kTheResult,
kTheRightMouseDown,
kTheRightMouseUp,
kTheRollOver,
kTheRomanLingo,
kTheRunMode,
kTheSafePlayer,
kTheScore,
kTheScummvmVersion, // set the Director version via lingo in tests
kTheSearchCurrentFolder,
kTheSearchPath,
kTheSearchPaths,
kTheSelection,
kTheSelEnd,
kTheSelStart,
kTheSerialNumber,
kTheShiftDown,
kTheSoundEntity,
kTheSoundEnabled,
kTheSoundKeepDevice,
kTheSoundLevel,
kTheSprite,
kTheSqrt,
kTheStage,
kTheStageBottom,
kTheStageColor,
kTheStageLeft,
kTheStageRight,
kTheStageTop,
kTheStillDown,
kTheSwitchColorDepth,
kTheTicks,
kTheTime,
kTheTimeoutKeyDown,
kTheTimeoutLapsed,
kTheTimeoutLength,
kTheTimeoutMouse,
kTheTimeoutPlay,
kTheTimeoutScript,
kTheTimer,
kTheTrace,
kTheTraceLoad,
kTheTraceLogFile,
kTheUpdateMovieEnabled,
kTheUserName,
kTheVideoForWindowsPresent,
kTheWindow,
kTheWindowList,
kTheWords,
kTheXtras,
kTheMaxTheEntityType // This must be always last
};
enum TheFieldType {
kTheNOField = 0,
kTheAbbr = 1,
kTheAntiAlias,
kTheAutoTab,
kTheBackColor,
kTheBlend,
kTheBorder,
kTheBottom,
kTheBoxDropShadow,
kTheBoxType,
kTheButtonType,
kTheCastLibNum,
kTheCastNum,
kTheCastType,
kTheCenter,
kTheChangeArea,
kTheChannelCount,
kTheCheckMark,
kTheChunkSize,
kTheConstraint,
kTheController,
kTheCrop,
kTheCuePointNames,
kTheCuePointTimes,
kTheCurrentTime,
kTheCursor,
kTheDepth,
kTheDigitalVideoType,
kTheDirectToStage,
kTheDrawRect,
kTheDropShadow,
kTheDuration,
kTheEditable,
kTheEditableText,
kTheEnabled,
kTheFileName,
kTheFilled,
kTheFlipH,
kTheFlipV,
kTheForeColor,
kTheFrameRate,
kTheHeight,
kTheHilite,
kTheImmediate,
kTheInk,
kTheInterface,
kTheLast,
kTheLeft,
kTheLineCount,
kTheLineSize,
kTheLoaded,
kTheLoc,
kTheLocH,
kTheLocV,
kTheLong,
kTheLoop,
kTheMargin,
kTheMedia,
kTheMediaBusy,
kTheMediaReady,
kTheMember,
kTheMemberNum,
kTheModal,
kTheModified,
kTheMostRecentCuePoint,
kTheMoveableSprite,
kTheMovieRate,
kTheMovieTime,
kTheName,
kTheNumber,
kThePageHeight,
kThePalette,
kThePaletteMapping,
kThePaletteRef,
kThePattern,
kThePausedAtStart,
kThePicture,
kThePreLoad,
kThePreLoadMode,
kThePuppet,
kThePurgePriority,
kTheRect,
kTheRegPoint,
kTheRight,
kTheSampleRate,
kTheSampleSize,
kTheScoreColor,
kTheScoreSelection,
kTheScript,
kTheScriptInstanceList,
kTheScriptNum,
kTheScriptText,
kTheScriptType,
kTheScriptsEnabled,
kTheSelectionField,
kTheSetTrackEnabled,
kTheShapeType,
kTheShort,
kTheSize,
kTheSound,
kTheSourceRect,
kTheSpriteNum,
kTheStartTime,
kTheStopTime,
kTheStrech,
kTheStretch,
kTheText,
kTheTextAlign,
kTheTextFont,
kTheTextHeight,
kTheTextSize,
kTheTextStyle,
kTheTimeScale,
kTheTitle,
kTheTitleVisible,
kTheTop,
kTheTrackEnabled,
kTheTrackNextKeyTime,
kTheTrackNextSampleTime,
kTheTrackPreviousKeyTime,
kTheTrackPreviousSampleTime,
kTheTrackText,
kTheTrails,
kTheTransitionType,
kTheTweened,
kTheType,
kTheUpdateLock,
kTheVideo,
kTheVisibility,
kTheVisible,
kTheVolume,
kTheWidth,
kTheWindowType,
kTheWordWrap,
kTheScrollTop,
kTheMaxTheFieldType // This must be always last
};
struct TheEntity {
TheEntityType entity;
const char *name;
bool hasId;
int version;
bool isFunction;
};
struct TheEntityField {
TheEntityType entity;
const char *name;
TheFieldType field;
int version;
};
} // End of namespace Director
#endif

View File

@@ -0,0 +1,126 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "director/director.h"
#include "director/lingo/lingo.h"
namespace Director {
static const struct CharNormProto {
Common::u32char_type_t from;
const char *to;
} charNormProtos[] = {
{ 0x00C0, "a" }, // À - \xCB in Mac Roman, \xC0 in Windows-1252
{ 0x00C1, "a" }, // Á - \xE7 in Mac Roman, \xC1 in Windows-1252
{ 0x00C2, "a" }, // Â - \xE5 in Mac Roman, \xC2 in Windows-1252
{ 0x00C3, "a" }, // Ã - \xCC in Mac Roman, \xC3 in Windows-1252
{ 0x00C4, "a" }, // Ä - \x80 in Mac Roman, \xC4 in Windows-1252
{ 0x00C5, "a" }, // Å - \x81 in Mac Roman, \xC5 in Windows-1252
{ 0x00C6, "ae" }, // Æ - \xAE in Mac Roman, \xC6 in Windows-1252
{ 0x00C7, "c" }, // Ç - \x82 in Mac Roman, \xC7 in Windows-1252
{ 0x00C8, "e" }, // È - \xE9 in Mac Roman, \xC8 in Windows-1252
{ 0x00C9, "e" }, // É - \x83 in Mac Roman, \xC9 in Windows-1252
{ 0x00CA, "e" }, // Ê - \xE6 in Mac Roman, \xCA in Windows-1252
{ 0x00CB, "e" }, // Ë - \xE8 in Mac Roman, \xCB in Windows-1252
{ 0x00CC, "i" }, // Ì - \xED in Mac Roman, \xCC in Windows-1252
{ 0x00CD, "i" }, // Í - \xEA in Mac Roman, \xCD in Windows-1252
{ 0x00CE, "i" }, // Î - \xEB in Mac Roman, \xCE in Windows-1252
{ 0x00CF, "i" }, // Ï - \xEC in Mac Roman, \xCF in Windows-1252
{ 0x00D0, "d" }, // Ð - \xD0 in Windows-1252
{ 0x00D1, "n" }, // Ñ - \x84 in Mac Roman, \xD1 in Windows-1252
{ 0x00D2, "o" }, // Ò - \xF1 in Mac Roman, \xD2 in Windows-1252
{ 0x00D3, "o" }, // Ó - \xEE in Mac Roman, \xD3 in Windows-1252
{ 0x00D4, "o" }, // Ô - \xEF in Mac Roman, \xD4 in Windows-1252
{ 0x00D5, "o" }, // Õ - \xCD in Mac Roman, \xD5 in Windows-1252
{ 0x00D6, "o" }, // Ö - \x85 in Mac Roman, \xD6 in Windows-1252
{ 0x00D8, "o" }, // Ø - \xAF in Mac Roman, \xD8 in Windows-1252
{ 0x00D9, "u" }, // Ù - \xF4 in Mac Roman, \xD9 in Windows-1252
{ 0x00DA, "u" }, // Ú - \xF2 in Mac Roman, \xDA in Windows-1252
{ 0x00DB, "u" }, // Û - \xF3 in Mac Roman, \xDB in Windows-1252
{ 0x00DC, "u" }, // Ü - \x86 in Mac Roman, \xDC in Windows-1252
{ 0x00DD, "y" }, // Ý - \xDD in Windows-1252
{ 0x00DE, "\xC3\xBE" }, // Þ - \xDE in Windows-1252
{ 0x00DF, "s" }, // ß - \xDF in Windows-1252
{ 0x00E0, "a" }, // à - \x88 in Mac Roman, \xE0 in Windows-1252
{ 0x00E1, "a" }, // á - \x87 in Mac Roman, \xE1 in Windows-1252
{ 0x00E2, "a" }, // â - \x89 in Mac Roman, \xE2 in Windows-1252
{ 0x00E3, "a" }, // ã - \x8B in Mac Roman, \xE3 in Windows-1252
{ 0x00E4, "a" }, // ä - \x8A in Mac Roman, \xE4 in Windows-1252
{ 0x00E5, "a" }, // å - \x8C in Mac Roman, \xE5 in Windows-1252
{ 0x00E6, "ae" }, // æ - \xBE in Mac Roman, \xE6 in Windows-1252
{ 0x00E7, "c" }, // ç - \x8D in Mac Roman, \xE7 in Windows-1252
{ 0x00E8, "e" }, // è - \x8F in Mac Roman, \xE8 in Windows-1252
{ 0x00E9, "e" }, // é - \x8E in Mac Roman, \xE9 in Windows-1252
{ 0x00EA, "e" }, // ê - \x90 in Mac Roman, \xEA in Windows-1252
{ 0x00EB, "e" }, // ë - \x91 in Mac Roman, \xEB in Windows-1252
{ 0x00EC, "i" }, // ì - \x93 in Mac Roman, \xEC in Windows-1252
{ 0x00ED, "i" }, // í - \x92 in Mac Roman, \xED in Windows-1252
{ 0x00EE, "i" }, // î - \x94 in Mac Roman, \xEE in Windows-1252
{ 0x00EF, "i" }, // ï - \x95 in Mac Roman, \xEF in Windows-1252
{ 0x00F0, "d" }, // ð - \xF0 in Windows-1252
{ 0x00F1, "n" }, // ñ - \x96 in Mac Roman, \xF1 in Windows-1252
{ 0x00F2, "o" }, // ò - \x98 in Mac Roman, \xF2 in Windows-1252
{ 0x00F3, "o" }, // ó - \x97 in Mac Roman, \xF3 in Windows-1252
{ 0x00F4, "o" }, // ô - \x99 in Mac Roman, \xF4 in Windows-1252
{ 0x00F5, "o" }, // õ - \x9B in Mac Roman, \xF5 in Windows-1252
{ 0x00F6, "o" }, // ö - \x9A in Mac Roman, \xF6 in Windows-1252
{ 0x00F8, "o" }, // ø - \xBF in Mac Roman, \xF8 in Windows-1252
{ 0x00F9, "u" }, // ù - \x9D in Mac Roman, \xF9 in Windows-1252
{ 0x00FA, "u" }, // ú - \x9C in Mac Roman, \xFA in Windows-1252
{ 0x00FB, "u" }, // û - \x9E in Mac Roman, \xFB in Windows-1252
{ 0x00FC, "u" }, // ü - \x9F in Mac Roman, \xFC in Windows-1252
{ 0x00FD, "y" }, // ý - \xFD in Windows-1252
{ 0x00FF, "y" }, // ÿ - \xD8 in Mac Roman
{ 0x0131, "i" }, // ı - \xF5 in Mac Roman
{ 0x0152, "oe" }, // Œ - \xCE in Mac Roman, \x8C in Windows-1252
{ 0x0153, "oe" }, // œ - \xCF in Mac Roman, \x9C in Windows-1252
{ 0x0160, "s" }, // Š - \x8A in Windows-1252
{ 0x0161, "s" }, // š - \x9A in Windows-1252
{ 0x0178, "y" }, // Ÿ - \xD9 in Mac Roman, \x9F in Windows-1252
{ 0x017D, "z" }, // Ž - \x8E in Windows-1252
{ 0x017E, "z" }, // ž - \x9E in Windows-1252
{ 0xFB01, "fi" }, // fi - \xDE in Mac Roman
{ 0xFB02, "fl" }, // fl - \xDF in Mac Roman
{ 0, NULL }
};
void Lingo::initCharNormalizations() {
for (char ch = 'A'; ch <= 'Z'; ch++) {
_charNormalizations[ch] = Common::U32String(Common::String(tolower(ch)), Common::kUtf8);
}
for (const CharNormProto *norm = charNormProtos; norm->to; norm++) {
_charNormalizations[norm->from] = Common::U32String(norm->to, Common::kUtf8);
}
}
Common::String Lingo::normalizeString(const Common::String &str) {
Common::U32String u32Str = str.decode(Common::kUtf8);
Common::U32String res;
for (const Common::u32char_type_t *ch = u32Str.c_str(); *ch; ch++) {
if (_charNormalizations.contains(*ch))
res += _charNormalizations[*ch];
else
res += *ch;
}
return res.encode(Common::kUtf8);
}
} // End of namespace Director

View File

@@ -0,0 +1,82 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_LINGO_UTILS_H
#define DIRECTOR_LINGO_LINGO_UTILS_H
#define ARGNUMCHECK(n) \
if (nargs != (n)) { \
warning("BUILDBOT: %s: expected %d argument%s, got %d", __FUNCTION__, (n), ((n) == 1 ? "" : "s"), nargs); \
g_lingo->dropStack(nargs); \
return; \
}
#define TYPECHECK(datum,t) \
if ((datum).type != (t)) { \
warning("BUILDBOT: %s: %s arg should be of type %s, not %s", __FUNCTION__, #datum, #t, (datum).type2str()); \
return; \
}
#define TYPECHECK2(datum, t1, t2) \
if ((datum).type != (t1) && (datum).type != (t2)) { \
warning("BUILDBOT: %s: %s arg should be of type %s or %s, not %s", __FUNCTION__, #datum, #t1, #t2, (datum).type2str()); \
return; \
}
#define TYPECHECK3(datum, t1, t2, t3) \
if ((datum).type != (t1) && (datum).type != (t2) && (datum).type != (t3)) { \
warning("BUILDBOT: %s: %s arg should be of type %s, %s, or %s, not %s", __FUNCTION__, #datum, #t1, #t2, #t3, (datum).type2str()); \
return; \
}
#define TYPECHECK4(datum, t1, t2, t3, t4) \
if ((datum).type != (t1) && (datum).type != (t2) && (datum).type != (t3) && (datum).type != (t4)) { \
warning("BUILDBOT: %s: %s arg should be of type %s, %s, %s, or %s, not %s", __FUNCTION__, #datum, #t1, #t2, #t3, #t4, (datum).type2str()); \
return; \
}
#define ARRBOUNDSCHECK(idx,array) \
if ((idx)-1 < 0 || (idx) > (int)(array).u.farr->arr.size()) { \
g_lingo->lingoError("%s: index out of bounds (%d of %d)", __FUNCTION__, (idx), (array).u.farr->arr.size()); \
return; \
}
#define XOBJSTUB(methname,retval) \
void methname(int nargs) { \
g_lingo->printSTUBWithArglist(#methname, nargs); \
g_lingo->dropStack(nargs); \
g_lingo->push(Datum(retval)); \
}
#define XOBJSTUBV(methname) \
void methname(int nargs) { \
g_lingo->printSTUBWithArglist(#methname, nargs); \
g_lingo->dropStack(nargs); \
g_lingo->push(Datum()); \
}
#define XOBJSTUBNR(methname) \
void methname(int nargs) { \
g_lingo->printSTUBWithArglist(#methname, nargs); \
g_lingo->dropStack(nargs); \
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,613 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_LINGO_H
#define DIRECTOR_LINGO_LINGO_H
namespace Audio {
class AudioStream;
}
namespace Common {
class SeekableReadStreamEndian;
}
namespace Director {
struct ChunkReference;
struct MenuReference;
struct PictureReference;
struct TheEntity;
struct TheEntityField;
struct LingoArchive;
struct LingoV4Bytecode;
struct LingoV4TheEntity;
struct Node;
struct Picture;
class AbstractObject;
class Cast;
class ScriptContext;
class DirectorEngine;
class Frame;
class LingoCompiler;
struct Breakpoint;
typedef void (*inst)(void);
#define STOP (inst)0
#define ENTITY_INDEX(t,id) ((t) * 100000 + (id))
int calcStringAlignment(const char *s);
int calcCodeAlignment(int l);
typedef Common::Array<inst> ScriptData;
struct FuncDesc {
Common::String name;
const char *proto;
FuncDesc(Common::String n, const char *p) { name = n; proto = p; }
};
typedef Common::HashMap<void *, FuncDesc *> FuncHash;
typedef Common::HashMap<Common::String, bool, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> MethodHash;
struct BuiltinProto {
const char *name;
void (*func)(int);
int minArgs; // -1 -- arglist
int maxArgs;
int version;
SymbolType type;
};
struct Symbol { /* symbol table entry */
Common::String *name;
SymbolType type;
union {
ScriptData *defn; /* HANDLER */
void (*func)(); /* OPCODE */
void (*bltin)(int); /* BUILTIN */
Common::String *s; /* STRING */
} u;
int *refCount;
int nargs; /* number of arguments */
int maxArgs; /* maximal number of arguments, for builtins */
int targetType; /* valid target objects, for method builtins */
Common::Array<Common::String> *argNames;
Common::Array<Common::String> *varNames;
ScriptContext *ctx; /* optional script context to execute with */
AbstractObject *target; /* optional method target */
bool anonymous;
Symbol();
Symbol(const Symbol &s);
Symbol& operator=(const Symbol &s);
bool operator==(Symbol &s) const;
void reset();
~Symbol();
};
struct PArray {
bool _sorted;
PropertyArray arr;
PArray() : _sorted(false) {}
PArray(int size) : _sorted(false), arr(size) {}
};
struct FArray {
bool _sorted;
DatumArray arr;
FArray() : _sorted(false) {}
FArray(int size) : _sorted(false), arr(size) {}
};
struct Datum { /* interpreter stack type */
DatumType type;
union {
int i; /* INT, ARGC, ARGCNORET */
double f; /* FLOAT */
Common::String *s; /* STRING, VARREF, OBJECT */
FArray *farr; /* ARRAY, POINT, RECT */
PArray *parr; /* PARRAY */
AbstractObject *obj; /* OBJECT */
ChunkReference *cref; /* CHUNKREF */
CastMemberID *cast; /* CASTREF, FIELDREF */
MenuReference *menu; /* MENUREF */
PictureReference *picture; /* PICTUREREF */
} u;
int *refCount;
bool ignoreGlobal; // True if this Datum should be ignored by showGlobals and clearGlobals
Datum();
Datum(const Datum &d);
Datum& operator=(const Datum &d);
Datum(int val);
Datum(double val);
Datum(const Common::String &val);
Datum(AbstractObject *val);
Datum(CastMember *val);
Datum(const CastMemberID &val);
Datum(const Common::Point &point);
Datum(const Common::Rect &rect);
void reset();
~Datum() {
reset();
}
Datum eval() const;
double asFloat() const;
int asInt() const;
Common::String asString(bool printonly = false) const;
CastMemberID asMemberID(CastType castType = kCastTypeAny, int castLib = 0) const;
Common::Point asPoint() const;
Datum clone() const;
bool isRef() const;
bool isVarRef() const;
bool isCastRef() const;
bool isArray() const;
bool isNumeric() const;
bool isVoid() const { return type == VOID; }
const char *type2str(bool ilk = false) const;
int equalTo(const Datum &d, bool ignoreCase = false) const;
uint32 compareTo(const Datum &d) const;
bool operator==(const Datum &d) const;
bool operator>(const Datum &d) const;
bool operator<(const Datum &d) const;
bool operator>=(const Datum &d) const;
bool operator<=(const Datum &d) const;
};
struct ChunkReference {
Datum source;
ChunkType type;
int startChunk;
int endChunk;
int start;
int end;
ChunkReference(const Datum &src, ChunkType t, int sc, int ec, int s, int e)
: source(src), type(t), startChunk(sc), endChunk(ec), start(s), end(e) {}
};
struct MenuReference {
int menuIdNum;
Common::String *menuIdStr;
int menuItemIdNum;
Common::String *menuItemIdStr;
MenuReference();
};
struct PictureReference {
Picture *_picture = nullptr;
~PictureReference();
};
struct PCell {
Datum p;
Datum v;
PCell();
PCell(const Datum &prop, const Datum &val);
};
struct Builtin {
void (*func)(void);
int nargs;
Builtin(void (*func1)(void), int nargs1) : func(func1), nargs(nargs1) {}
};
typedef Common::HashMap<int32, ScriptContext *> ScriptContextHash;
typedef Common::HashMap<int32, Common::HashMap<Common::String, ScriptContext *> *> FactoryContextHash;
typedef Common::Array<Datum> StackData;
typedef Common::HashMap<Common::String, Symbol, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SymbolHash;
typedef Common::HashMap<Common::String, Datum, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> DatumHash;
typedef Common::HashMap<Common::String, Builtin *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> BuiltinHash;
typedef Common::HashMap<Common::String, VarType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> VarTypeHash;
typedef void (*XLibOpenerFunc)(ObjectType, const Common::Path &);
typedef void (*XLibCloserFunc)(ObjectType);
typedef Common::HashMap<Common::String, XLibOpenerFunc, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> XLibOpenerFuncHash;
typedef Common::HashMap<Common::String, XLibCloserFunc, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> XLibCloserFuncHash;
typedef Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> XLibTypeHash;
typedef Common::HashMap<Common::String, ObjectType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> OpenXLibsHash;
typedef Common::HashMap<Common::String, AbstractObject *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> OpenXLibsStateHash;
typedef Common::HashMap<Common::String, const TheEntity *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TheEntityHash;
typedef Common::HashMap<Common::String, const TheEntityField *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TheEntityFieldHash;
struct CFrame { /* proc/func call stack frame */
Symbol sp; /* symbol table entry */
int retPC; /* where to resume after return */
ScriptData *retScript; /* which script to resume after return */
ScriptContext *retContext; /* which script context to use after return */
DatumHash *retLocalVars;
Datum retMe; /* which me obj to use after return */
uint stackSizeBefore;
bool allowRetVal; /* whether to allow a return value */
Datum defaultRetVal; /* default return value */
int paramCount; /* original number of arguments submitted */
Common::Array<Datum> paramList; /* original argument list */
};
struct LingoEvent {
LEvent event;
int eventId;
EventHandlerSourceType eventHandlerSourceType;
ScriptType scriptType;
bool passByDefault;
uint16 channelId;
CastMemberID scriptId;
Common::Point mousePos;
int behaviorIndex;
AbstractObject *scriptInstance;
LingoEvent(LEvent e, int ei, ScriptType st, bool pass, CastMemberID si = CastMemberID(), Common::Point mp = Common::Point(-1, -1), int bi = -1) {
event = e;
eventId = ei;
eventHandlerSourceType = kNoneHandler;
scriptType = st;
passByDefault = pass;
channelId = 0;
scriptId = si;
mousePos = mp;
behaviorIndex = bi;
scriptInstance = nullptr;
}
LingoEvent(LEvent e, int ei, EventHandlerSourceType ehst, bool pass, Common::Point mp = Common::Point(-1, -1), uint16 ci = 0, int bi = -1) {
event = e;
eventId = ei;
eventHandlerSourceType = ehst;
scriptType = kNoneScript;
passByDefault = pass;
channelId = ci;
scriptId = CastMemberID();
mousePos = mp;
behaviorIndex = bi;
scriptInstance = nullptr;
}
};
struct LingoArchive {
LingoArchive(Cast *c) : cast(c) {};
~LingoArchive();
Cast *cast;
ScriptContextHash lctxContexts;
ScriptContextHash scriptContexts[kMaxScriptType + 1];
FactoryContextHash factoryContexts;
Common::Array<Common::String> names;
Common::HashMap<uint32, Common::String> primaryEventHandlers;
SymbolHash functionHandlers;
ScriptContext *getScriptContext(ScriptType type, uint16 id);
ScriptContext *findScriptContext(uint16 id);
Common::String getName(uint16 id);
Common::String formatFunctionList(const char *prefix);
void addCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr, uint32 preprocFlags = kLPPNone);
void patchCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr, uint32 preprocFlags = kLPPNone);
void removeCode(ScriptType type, uint16 id);
void replaceCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr);
void addCodeV4(Common::SeekableReadStreamEndian &stream, uint16 lctxIndex, const Common::String &archName, uint16 version);
void addNamesV4(Common::SeekableReadStreamEndian &stream);
// lingo-patcher.cpp
void patchScriptHandler(ScriptType type, CastMemberID id);
};
struct LingoState {
// Execution state for a Lingo process, created every time
// a top-level handler is called (e.g. on mouseDown).
// Can be swapped out when another script gets called with priority.
// Call frames are pushed and popped from the callstack with
// pushContext and popContext.
Common::Array<CFrame *> callstack; // call stack
uint pc = 0; // current program counter
ScriptData *script = nullptr; // current Lingo script
ScriptContext *context = nullptr; // current Lingo script context
DatumHash *localVars = nullptr; // current local variables
Datum me; // current me object
StackData stack;
~LingoState();
};
enum LingoExecState {
kRunning,
kPause,
};
class Lingo {
public:
Lingo(DirectorEngine *vm);
~Lingo();
void resetLingo();
void cleanupLingo();
void resetLingoGo();
int getMenuNum();
int getMenuItemsNum(Datum &d);
int getXtrasNum();
int getCastLibsNum();
int getMembersNum(uint16 castLibID);
void executeHandler(const Common::String &name, int numargs = 0);
void executeScript(ScriptType type, CastMemberID id);
Common::String formatStack();
void printStack(const char *s, uint pc);
Common::String formatCallStack(uint pc);
void printCallStack(uint pc);
Common::String formatFrame();
Common::String formatCurrentInstruction();
Common::String decodeInstruction(ScriptData *sd, uint pc, uint *newPC = NULL);
Common::String decodeScript(ScriptData *sd);
Common::String formatFunctionName(Symbol &sym);
Common::String formatFunctionBody(Symbol &sym);
void reloadBuiltIns();
void initBuiltIns();
void initBuiltIns(const BuiltinProto protos[]);
void cleanupBuiltIns();
void cleanupBuiltIns(const BuiltinProto protos[]);
void initFuncs();
void cleanupFuncs();
void initBytecode();
void initMethods();
void cleanupMethods();
void initXLibs();
void cleanupXLibs();
Common::String normalizeXLibName(Common::String name);
void openXLib(Common::String name, ObjectType type, const Common::Path &path);
void closeXLib(Common::String name);
void closeOpenXLibs();
void reloadOpenXLibs();
void runTests();
// lingo-events.cpp
private:
void initEventHandlerTypes();
bool processEvent(LEvent event, ScriptType st, CastMemberID scriptId, int channelId = -1, AbstractObject *obj = nullptr);
public:
ScriptType event2script(LEvent ev);
Symbol getHandler(const Common::String &name);
void processEvents(Common::Queue<LingoEvent> &queue, bool isInputEvent);
public:
bool execute(int targetFrame = -1);
void switchStateFromWindow();
void freezeState();
void freezePlayState();
void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal, int paramCount, int nargs);
void popContext(bool aborting = false);
void cleanLocalVars();
void varAssign(const Datum &var, const Datum &value);
Datum varFetch(const Datum &var, bool silent = false);
Common::U32String evalChunkRef(const Datum &var);
Datum findVarV4(int varType, const Datum &id);
CastMemberID resolveCastMember(const Datum &memberID, const Datum &castLib, CastType type);
CastMemberID toCastMemberID(const Datum &member, const Datum &castLib);
void exposeXObject(const char *name, Datum obj);
int getAlignedType(const Datum &d1, const Datum &d2, bool equality);
Common::String formatAllVars();
void printAllVars();
inst readInst() { return getInst(_state->pc++); }
inst getInst(uint pc) { return (*_state->script)[pc]; }
int readInt() { return getInt(_state->pc++); }
int getInt(uint pc);
double readFloat() { double d = getFloat(_state->pc); _state->pc += calcCodeAlignment(sizeof(double)); return d; }
double getFloat(uint pc) { return *(double *)(&((*_state->script)[_state->pc])); }
char *readString() { char *s = getString(_state->pc); _state->pc += calcStringAlignment(s); return s; }
char *getString(uint pc) { return (char *)(&((*_state->script)[_state->pc])); }
Datum getVoid();
void pushVoid();
void printArgs(const char *funcname, int nargs, const char *prefix = nullptr);
inline void printSTUBWithArglist(const char *funcname, int nargs) { printArgs(funcname, nargs, "STUB: "); }
void convertVOIDtoString(int arg, int nargs);
void dropStack(int nargs);
void drop(uint num);
void lingoError(const char *s, ...);
void func_mci(const Common::String &name);
void func_mciwait(const Common::String &name);
void func_beep(int repeats);
void func_goto(Datum &frame, Datum &movie, bool commandgo = false );
void func_gotoloop();
void func_gotonext();
void func_gotoprevious();
void func_play(Datum &frame, Datum &movie);
void func_playdone();
void func_cursor(Datum cursorDatum);
int func_marker(int m);
uint16 func_label(Datum &label);
// lingo-the.cpp
public:
void initTheEntities();
void cleanUpTheEntities();
const char *entity2str(int id);
const char *field2str(int id);
// global kTheEntity
Datum _actorList;
Common::u32char_type_t _itemDelimiter;
bool _exitLock;
bool _preLoadEventAbort; // no-op, everything is always preloaded
Datum _searchPath;
bool _trace; // state of movie's trace function
int _traceLoad; // internal Director verbosity level
bool _updateMovieEnabled;
bool _romanLingo;
Datum getTheEntity(int entity, Datum &id, int field);
void setTheEntity(int entity, Datum &id, int field, Datum &d);
Datum getTheSprite(Datum &id, int field);
void setTheSprite(Datum &id, int field, Datum &d);
Datum getTheCast(Datum &id, int field);
void setTheCast(Datum &id, int field, Datum &d);
Datum getTheCastLib(Datum &id, int field);
void setTheCastLib(Datum &id, int field, Datum &d);
Datum getTheField(Datum &id1, int field);
void setTheField(Datum &id1, int field, Datum &d);
Datum getTheChunk(Datum &chunk, int field);
void setTheChunk(Datum &chunk, int field, Datum &d);
void getObjectProp(Datum &obj, Common::String &propName);
void setObjectProp(Datum &obj, Common::String &propName, Datum &d);
Datum getTheDate(int field);
Datum getTheTime(int field);
Datum getTheDeskTopRectList();
private:
Common::StringArray _entityNames;
Common::StringArray _fieldNames;
public:
LingoCompiler *_compiler;
LingoState *_state;
int _currentChannelId;
bool _freezeState;
bool _freezePlay;
bool _playDone;
bool _abort;
bool _expectError;
bool _caughtError;
TheEntityHash _theEntities;
TheEntityFieldHash _theEntityFields;
int _objectEntityId;
SymbolHash _builtinCmds;
SymbolHash _builtinFuncs;
SymbolHash _builtinConsts;
SymbolHash _builtinListHandlers;
SymbolHash _methods;
XLibOpenerFuncHash _xlibOpeners;
XLibCloserFuncHash _xlibClosers;
XLibTypeHash _xlibTypes;
OpenXLibsHash _openXLibs;
OpenXLibsStateHash _openXLibsState;
Common::StringArray _openXtras;
Common::Array<Datum> _openXtraObjects;
OpenXLibsStateHash _openXtrasState;
Common::String _floatPrecisionFormat;
public:
void push(Datum d);
Datum pop();
Datum peek(uint offset);
public:
Common::HashMap<uint32, const char *> _eventHandlerTypes;
Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _eventHandlerTypeIds;
Common::HashMap<Common::String, Audio::AudioStream *> _audioAliases;
DatumHash _globalvars;
FuncHash _functions;
Common::HashMap<int, const LingoV4Bytecode *> _lingoV4;
Common::HashMap<int, const LingoV4TheEntity *> _lingoV4TheEntity;
uint _globalCounter;
DirectorEngine *_vm;
int _floatPrecision;
Datum _theResult;
// events
bool _passEvent;
Datum _perFrameHook;
Datum _windowList;
Symbol _currentInputEvent;
struct {
LingoExecState _state = kRunning;
bool (*_shouldPause)() = nullptr;
} _exec;
public:
void executeImmediateScripts(Frame *frame);
void executePerFrameHook(int frame, int subframe, bool stepFrame = true);
// lingo-utils.cpp
private:
Common::HashMap<uint32, Common::U32String> _charNormalizations;
void initCharNormalizations();
public:
Common::String normalizeString(const Common::String &str);
public:
void addBreakpoint(Breakpoint &bp);
bool delBreakpoint(int id);
Breakpoint *getBreakpoint(int id);
const Common::Array<Breakpoint> &getBreakpoints() const { return _breakpoints; }
Common::Array<Breakpoint> &getBreakpoints() { return _breakpoints; }
private:
int _bpNextId = 1;
Common::Array<Breakpoint> _breakpoints;
};
extern Lingo *g_lingo;
} // End of namespace Director
#endif

View File

@@ -0,0 +1,12 @@
This directory is coming straight from the ProjectorRays project
https://github.com/ProjectorRays/ProjectorRays/tree/master/src/lingodec
And should be kept in sync.
At the time of writing, the changes have not yet been merged within
ProjectorRays and as such, live in 'scummvm' branch:
https://github.com/ProjectorRays/ProjectorRays/tree/scummvm/src/lingodec
The original code is licensed under the Mozilla Public License 2.0.

View File

@@ -0,0 +1,274 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/util.h"
#include "./ast.h"
#include "./handler.h"
#include "./names.h"
#include "./script.h"
namespace LingoDec {
/* Datum */
int Datum::toInt() {
switch (type) {
case kDatumInt:
return i;
case kDatumFloat:
return f;
default:
break;
}
return 0;
}
/* AST */
void AST::addStatement(Common::SharedPtr<Node> statement) {
currentBlock->addChild(Common::move(statement));
}
void AST::enterBlock(BlockNode *block) {
currentBlock = block;
}
void AST::exitBlock() {
auto ancestorStatement = currentBlock->ancestorStatement();
if (!ancestorStatement) {
currentBlock = nullptr;
return;
}
ancestorStatement->_endOffset = currentBlock->_endOffset;
auto block = ancestorStatement->parent;
if (!block || block->type != kBlockNode) {
currentBlock = nullptr;
return;
}
currentBlock = static_cast<BlockNode *>(block);
}
/* Node */
Common::SharedPtr<Datum> Node::getValue() {
return Common::SharedPtr<Datum>(new Datum());
}
Node *Node::ancestorStatement() {
Node *ancestor = parent;
while (ancestor && !ancestor->isStatement) {
ancestor = ancestor->parent;
}
return ancestor;
}
LoopNode *Node::ancestorLoop() {
Node *ancestor = parent;
while (ancestor && !ancestor->isLoop) {
ancestor = ancestor->parent;
}
return static_cast<LoopNode *>(ancestor);
}
bool Node::hasSpaces(bool) {
return true;
}
/* ErrorNode */
bool ErrorNode::hasSpaces(bool) {
return false;
}
/* LiteralNode */
Common::SharedPtr<Datum> LiteralNode::getValue() {
return value;
}
bool LiteralNode::hasSpaces(bool) {
return false;
}
/* BlockNode */
void BlockNode::addChild(Common::SharedPtr<Node> child) {
child->parent = this;
children.push_back(Common::move(child));
}
/* BinaryOpNode */
unsigned int BinaryOpNode::getPrecedence() const {
switch (opcode) {
case kOpMul:
case kOpDiv:
case kOpMod:
return 1;
case kOpAdd:
case kOpSub:
return 2;
case kOpLt:
case kOpLtEq:
case kOpNtEq:
case kOpEq:
case kOpGt:
case kOpGtEq:
return 3;
case kOpAnd:
return 4;
case kOpOr:
return 5;
default:
break;
}
return 0;
}
/* MemberExprNode */
bool MemberExprNode::hasSpaces(bool dot) {
return !dot;
}
/* VarNode */
bool VarNode::hasSpaces(bool) {
return false;
}
/* CaseStmtNode */
void CaseStmtNode::addOtherwise(uint32 offset) {
otherwise = Common::SharedPtr<OtherwiseNode>(new OtherwiseNode(offset));
otherwise->parent = this;
otherwise->block->endPos = endPos;
}
/* CallNode */
bool CallNode::noParens() const {
if (isStatement) {
// TODO: Make a complete list of commonly paren-less commands
if (name == "put")
return true;
if (name == "return")
return true;
}
return false;
}
bool CallNode::isMemberExpr() const {
if (isExpression) {
size_t nargs = argList->getValue()->l.size();
if (name == "cast" && (nargs == 1 || nargs == 2))
return true;
if (name == "member" && (nargs == 1 || nargs == 2))
return true;
if (name == "script" && (nargs == 1 || nargs == 2))
return true;
if (name == "castLib" && nargs == 1)
return true;
if (name == "window" && nargs == 1)
return true;
}
return false;
}
bool CallNode::hasSpaces(bool dot) {
if (!dot && isMemberExpr())
return true;
if (noParens())
return true;
return false;
}
/* ObjCallNode */
bool ObjCallNode::hasSpaces(bool) {
return false;
}
/* ObjCallV4Node */
bool ObjCallV4Node::hasSpaces(bool) {
return false;
}
/* ObjPropExprNode */
bool ObjPropExprNode::hasSpaces(bool dot) {
return !dot;
}
/* ObjBracketExprNode */
bool ObjBracketExprNode::hasSpaces(bool) {
return false;
}
/* ObjPropIndexExprNode */
bool ObjPropIndexExprNode::hasSpaces(bool) {
return false;
}
void ErrorNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CommentNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void LiteralNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void IfStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void NewObjNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void EndCaseNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void HandlerNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjCallNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void PutStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void TheExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void BinaryOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CaseStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ExitStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void TellStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void WhenStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CaseLabelNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ChunkExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void InverseOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjCallV4Node::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void OtherwiseNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void MemberExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void PlayCmdStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ThePropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void MenuPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SoundCmdStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SoundPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void AssignmentStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ExitRepeatStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void NextRepeatStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjBracketExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SpritePropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ChunkDeleteStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ChunkHiliteStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void RepeatWhileStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void MenuItemPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjPropIndexExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void RepeatWithInStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void RepeatWithToStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SpriteWithinExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void LastStringChunkExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SpriteIntersectsExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void StringChunkCountExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void VarNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CallNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void BlockNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void NotOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
} // namespace LingoDec

View File

@@ -0,0 +1,864 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_AST_H
#define LINGODEC_AST_H
#include "common/array.h"
#include "common/ptr.h"
#include "common/str.h"
#include "common/util.h"
#include "./enums.h"
namespace LingoDec {
struct CaseLabelNode;
struct Handler;
struct LoopNode;
struct Node;
struct RepeatWithInStmtNode;
/* Datum */
struct Datum {
DatumType type;
int i = -1;
double f = 0.0f;
Common::String s;
Common::Array<Common::SharedPtr<Node>> l;
Datum() {
type = kDatumVoid;
}
Datum(int val) {
type = kDatumInt;
i = val;
}
Datum(double val) {
type = kDatumFloat;
f = val;
}
Datum(DatumType t, Common::String val) {
type = t;
s = val;
}
Datum(DatumType t, Common::Array<Common::SharedPtr<Node>> val) {
type = t;
l = val;
}
int toInt();
};
class NodeVisitor;
/* Node */
struct Node {
NodeType type;
bool isExpression;
bool isStatement;
bool isLabel;
bool isLoop;
Node *parent;
uint32 _startOffset;
uint32 _endOffset;
Node(NodeType t, uint32 offset) : type(t), isExpression(false), isStatement(false), isLabel(false), isLoop(false), parent(nullptr), _startOffset(offset), _endOffset(offset) {}
virtual ~Node() {}
virtual void accept(NodeVisitor& visitor) const = 0;
virtual Common::SharedPtr<Datum> getValue();
Node *ancestorStatement();
LoopNode *ancestorLoop();
virtual bool hasSpaces(bool dot);
};
/* ExprNode */
struct ExprNode : Node {
ExprNode(NodeType t, uint32 offset) : Node(t, offset) {
isExpression = true;
}
};
/* StmtNode */
struct StmtNode : Node {
StmtNode(NodeType t, uint32 offset) : Node(t, offset) {
isStatement = true;
}
};
/* LabelNode */
struct LabelNode : Node {
LabelNode(NodeType t, uint32 offset) : Node(t, offset) {
isLabel = true;
}
};
/* LoopNode */
struct LoopNode : StmtNode {
uint32 startIndex;
LoopNode(NodeType t, uint32 startIndex_, uint32 offset) : StmtNode(t, offset), startIndex(startIndex_) {
isLoop = true;
}
};
/* ErrorNode */
struct ErrorNode : ExprNode {
explicit ErrorNode(uint32 offset) : ExprNode(kErrorNode, offset) {}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* CommentNode */
struct CommentNode : Node {
Common::String text;
CommentNode(uint32 offset, Common::String t) : Node(kCommentNode, offset), text(t) {}
void accept(NodeVisitor &visitor) const override;
};
/* LiteralNode */
struct LiteralNode : ExprNode {
Common::SharedPtr<Datum> value;
LiteralNode(uint32 offset, Common::SharedPtr<Datum> d) : ExprNode(kLiteralNode, offset) {
value = Common::move(d);
}
Common::SharedPtr<Datum> getValue() override;
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* BlockNode */
struct BlockNode : Node {
Common::Array<Common::SharedPtr<Node>> children;
// for use during translation:
uint32 endPos;
CaseLabelNode *currentCaseLabel = nullptr;
explicit BlockNode(uint32 offset) : Node(kBlockNode, offset), endPos(Common::String::npos) {}
void addChild(Common::SharedPtr<Node> child);
void accept(NodeVisitor &visitor) const override;
};
/* HandlerNode */
struct HandlerNode : Node {
Handler *handler;
Common::SharedPtr<BlockNode> block;
HandlerNode(uint32 offset, Handler *h)
: Node(kHandlerNode, offset), handler(h) {
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ExitStmtNode */
struct ExitStmtNode : StmtNode {
explicit ExitStmtNode(uint32 offset) : StmtNode(kExitStmtNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* InverseOpNode */
struct InverseOpNode : ExprNode {
Common::SharedPtr<Node> operand;
InverseOpNode(uint32 offset, Common::SharedPtr<Node> o) : ExprNode(kInverseOpNode, offset) {
operand = Common::move(o);
operand->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* NotOpNode */
struct NotOpNode : ExprNode {
Common::SharedPtr<Node> operand;
NotOpNode(uint32 offset, Common::SharedPtr<Node> o) : ExprNode(kNotOpNode, offset) {
operand = Common::move(o);
operand->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* BinaryOpNode */
struct BinaryOpNode : ExprNode {
OpCode opcode;
Common::SharedPtr<Node> left;
Common::SharedPtr<Node> right;
BinaryOpNode(uint32 offset, OpCode op, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
: ExprNode(kBinaryOpNode, offset), opcode(op) {
left = Common::move(a);
left->parent = this;
right = Common::move(b);
right->parent = this;
}
virtual unsigned int getPrecedence() const;
void accept(NodeVisitor &visitor) const override;
};
/* ChunkExprNode */
struct ChunkExprNode : ExprNode {
ChunkExprType type;
Common::SharedPtr<Node> first;
Common::SharedPtr<Node> last;
Common::SharedPtr<Node> string;
ChunkExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b, Common::SharedPtr<Node> s)
: ExprNode(kChunkExprNode, offset), type(t) {
first = Common::move(a);
first->parent = this;
last = Common::move(b);
last->parent = this;
string = Common::move(s);
string->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ChunkHiliteStmtNode */
struct ChunkHiliteStmtNode : StmtNode {
Common::SharedPtr<Node> chunk;
ChunkHiliteStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kChunkHiliteStmtNode, offset) {
chunk = Common::move(c);
chunk->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ChunkDeleteStmtNode */
struct ChunkDeleteStmtNode : StmtNode {
Common::SharedPtr<Node> chunk;
ChunkDeleteStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kChunkDeleteStmtNode, offset) {
chunk = Common::move(c);
chunk->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SpriteIntersectsExprNode */
struct SpriteIntersectsExprNode : ExprNode {
Common::SharedPtr<Node> firstSprite;
Common::SharedPtr<Node> secondSprite;
SpriteIntersectsExprNode(uint32 offset, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
: ExprNode(kSpriteIntersectsExprNode, offset) {
firstSprite = Common::move(a);
firstSprite->parent = this;
secondSprite = Common::move(b);
secondSprite->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SpriteWithinExprNode */
struct SpriteWithinExprNode : ExprNode {
Common::SharedPtr<Node> firstSprite;
Common::SharedPtr<Node> secondSprite;
SpriteWithinExprNode(uint32 offset, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
: ExprNode(kSpriteWithinExprNode, offset) {
firstSprite = Common::move(a);
firstSprite->parent = this;
secondSprite = Common::move(b);
secondSprite->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* MemberExprNode */
struct MemberExprNode : ExprNode {
Common::String type;
Common::SharedPtr<Node> memberID;
Common::SharedPtr<Node> castID;
MemberExprNode(uint32 offset, Common::String type_, Common::SharedPtr<Node> memberID_, Common::SharedPtr<Node> castID_)
: ExprNode(kMemberExprNode, offset), type(type_) {
this->memberID = Common::move(memberID_);
this->memberID->parent = this;
if (castID_) {
this->castID = Common::move(castID_);
this->castID->parent = this;
}
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* VarNode */
struct VarNode : ExprNode {
Common::String varName;
VarNode(uint32 offset, Common::String v) : ExprNode(kVarNode, offset), varName(v) {}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* AssignmentStmtNode */
struct AssignmentStmtNode : StmtNode {
Common::SharedPtr<Node> variable;
Common::SharedPtr<Node> value;
bool forceVerbose;
AssignmentStmtNode(uint32 offset, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val, bool forceVerbose_ = false)
: StmtNode(kAssignmentStmtNode, offset), forceVerbose(forceVerbose_) {
variable = Common::move(var);
variable->parent = this;
value = Common::move(val);
value->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* IfStmtNode */
struct IfStmtNode : StmtNode {
bool hasElse;
Common::SharedPtr<Node> condition;
Common::SharedPtr<BlockNode> block1;
Common::SharedPtr<BlockNode> block2;
IfStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kIfStmtNode, offset), hasElse(false) {
condition = Common::move(c);
condition->parent = this;
block1 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block1->parent = this;
block2 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block2->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* RepeatWhileStmtNode */
struct RepeatWhileStmtNode : LoopNode {
Common::SharedPtr<Node> condition;
Common::SharedPtr<BlockNode> block;
RepeatWhileStmtNode(uint32 startIndex_, Common::SharedPtr<Node> c, uint32 offset)
: LoopNode(kRepeatWhileStmtNode, startIndex_, offset) {
condition = Common::move(c);
condition->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* RepeatWithInStmtNode */
struct RepeatWithInStmtNode : LoopNode {
Common::String varName;
Common::SharedPtr<Node> list;
Common::SharedPtr<BlockNode> block;
RepeatWithInStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> l, uint32 offset)
: LoopNode(kRepeatWithInStmtNode, startIndex_, offset) {
varName = v;
list = Common::move(l);
list->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* RepeatWithToStmtNode */
struct RepeatWithToStmtNode : LoopNode {
Common::String varName;
Common::SharedPtr<Node> start;
bool up;
Common::SharedPtr<Node> end;
Common::SharedPtr<BlockNode> block;
RepeatWithToStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> s, bool _up, Common::SharedPtr<Node> e, uint32 offset)
: LoopNode(kRepeatWithToStmtNode, startIndex_, offset), up(_up) {
varName = v;
start = Common::move(s);
start->parent = this;
end = Common::move(e);
end->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* CaseLabelNode */
struct CaseLabelNode : LabelNode {
Common::SharedPtr<Node> value;
CaseExpect expect;
Common::SharedPtr<CaseLabelNode> nextOr;
Common::SharedPtr<CaseLabelNode> nextLabel;
Common::SharedPtr<BlockNode> block;
CaseLabelNode(uint32 offset, Common::SharedPtr<Node> v, CaseExpect e) : LabelNode(kCaseLabelNode, offset), expect(e) {
value = Common::move(v);
value->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* OtherwiseNode */
struct OtherwiseNode : LabelNode {
Common::SharedPtr<BlockNode> block;
explicit OtherwiseNode(uint32 offset) : LabelNode(kOtherwiseNode, offset) {
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* EndCaseNode */
struct EndCaseNode : LabelNode {
explicit EndCaseNode(uint32 offset) : LabelNode(kEndCaseNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* CaseStmtNode */
struct CaseStmtNode : StmtNode {
Common::SharedPtr<Node> value;
Common::SharedPtr<CaseLabelNode> firstLabel;
Common::SharedPtr<OtherwiseNode> otherwise;
// for use during translation:
int32 endPos = -1;
int32 potentialOtherwisePos = -1;
CaseStmtNode(uint32 offset, Common::SharedPtr<Node> v) : StmtNode(kCaseStmtNode, offset) {
value = Common::move(v);
value->parent = this;
}
void addOtherwise(uint32 offset);
void accept(NodeVisitor &visitor) const override;
};
/* TellStmtNode */
struct TellStmtNode : StmtNode {
Common::SharedPtr<Node> window;
Common::SharedPtr<BlockNode> block;
TellStmtNode(uint32 offset, Common::SharedPtr<Node> w) : StmtNode(kTellStmtNode, offset) {
window = Common::move(w);
window->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SoundCmdStmtNode */
struct SoundCmdStmtNode : StmtNode {
Common::String cmd;
Common::SharedPtr<Node> argList;
SoundCmdStmtNode(uint32 offset, Common::String c, Common::SharedPtr<Node> a) : StmtNode(kSoundCmdStmtNode, offset) {
cmd = c;
argList = Common::move(a);
argList->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* PlayCmdStmtNode */
struct PlayCmdStmtNode : StmtNode {
Common::SharedPtr<Node> argList;
PlayCmdStmtNode(uint32 offset, Common::SharedPtr<Node> a) : StmtNode(kPlayCmdStmtNode, offset) {
argList = Common::move(a);
argList->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* CallNode */
struct CallNode : Node {
Common::String name;
Common::SharedPtr<Node> argList;
CallNode(uint32 offset, Common::String n, Common::SharedPtr<Node> a) : Node(kCallNode, offset) {
name = n;
argList = Common::move(a);
argList->parent = this;
if (argList->getValue()->type == kDatumArgListNoRet)
isStatement = true;
else
isExpression = true;
}
bool noParens() const;
bool isMemberExpr() const;
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjCallNode */
struct ObjCallNode : Node {
Common::String name;
Common::SharedPtr<Node> argList;
ObjCallNode(uint32 offset, Common::String n, Common::SharedPtr<Node> a) : Node(kObjCallNode, offset) {
name = n;
argList = Common::move(a);
argList->parent = this;
if (argList->getValue()->type == kDatumArgListNoRet)
isStatement = true;
else
isExpression = true;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjCallV4Node */
struct ObjCallV4Node : Node {
Common::SharedPtr<Node> obj;
Common::SharedPtr<Node> argList;
ObjCallV4Node(uint32 offset, Common::SharedPtr<Node> o, Common::SharedPtr<Node> a) : Node(kObjCallV4Node, offset) {
obj = o;
argList = Common::move(a);
argList->parent = this;
if (argList->getValue()->type == kDatumArgListNoRet)
isStatement = true;
else
isExpression = true;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* TheExprNode */
struct TheExprNode : ExprNode {
Common::String prop;
TheExprNode(uint32 offset, Common::String p) : ExprNode(kTheExprNode, offset), prop(p) {}
void accept(NodeVisitor &visitor) const override;
};
/* LastStringChunkExprNode */
struct LastStringChunkExprNode : ExprNode {
ChunkExprType type;
Common::SharedPtr<Node> obj;
LastStringChunkExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> o)
: ExprNode(kLastStringChunkExprNode, offset), type(t) {
obj = Common::move(o);
obj->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* StringChunkCountExprNode */
struct StringChunkCountExprNode : ExprNode {
ChunkExprType type;
Common::SharedPtr<Node> obj;
StringChunkCountExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> o)
: ExprNode(kStringChunkCountExprNode, offset), type(t) {
obj = Common::move(o);
obj->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* MenuPropExprNode */
struct MenuPropExprNode : ExprNode {
Common::SharedPtr<Node> menuID;
unsigned int prop;
MenuPropExprNode(uint32 offset, Common::SharedPtr<Node> m, unsigned int p)
: ExprNode(kMenuPropExprNode, offset), prop(p) {
menuID = Common::move(m);
menuID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* MenuItemPropExprNode */
struct MenuItemPropExprNode : ExprNode {
Common::SharedPtr<Node> menuID;
Common::SharedPtr<Node> itemID;
unsigned int prop;
MenuItemPropExprNode(uint32 offset, Common::SharedPtr<Node> m, Common::SharedPtr<Node> i, unsigned int p)
: ExprNode(kMenuItemPropExprNode, offset), prop(p) {
menuID = Common::move(m);
menuID->parent = this;
itemID = Common::move(i);
itemID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SoundPropExprNode */
struct SoundPropExprNode : ExprNode {
Common::SharedPtr<Node> soundID;
unsigned int prop;
SoundPropExprNode(uint32 offset, Common::SharedPtr<Node> s, unsigned int p)
: ExprNode(kSoundPropExprNode, offset), prop(p) {
soundID = Common::move(s);
soundID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SpritePropExprNode */
struct SpritePropExprNode : ExprNode {
Common::SharedPtr<Node> spriteID;
unsigned int prop;
SpritePropExprNode(uint32 offset, Common::SharedPtr<Node> s, unsigned int p)
: ExprNode(kSpritePropExprNode, offset), prop(p) {
spriteID = Common::move(s);
spriteID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ThePropExprNode */
struct ThePropExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::String prop;
ThePropExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p)
: ExprNode(kThePropExprNode, offset), prop(p) {
obj = Common::move(o);
obj->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ObjPropExprNode */
struct ObjPropExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::String prop;
ObjPropExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p)
: ExprNode(kObjPropExprNode, offset), prop(p) {
obj = Common::move(o);
obj->parent = this;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjBracketExprNode */
struct ObjBracketExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::SharedPtr<Node> prop;
ObjBracketExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::SharedPtr<Node> p)
: ExprNode(kObjBracketExprNode, offset) {
obj = Common::move(o);
obj->parent = this;
prop = Common::move(p);
prop->parent = this;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjPropIndexExprNode */
struct ObjPropIndexExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::String prop;
Common::SharedPtr<Node> index;
Common::SharedPtr<Node> index2;
ObjPropIndexExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p, Common::SharedPtr<Node> i, Common::SharedPtr<Node> i2)
: ExprNode(kObjPropIndexExprNode, offset), prop(p) {
obj = Common::move(o);
obj->parent = this;
index = Common::move(i);
index->parent = this;
if (i2) {
index2 = Common::move(i2);
index2->parent = this;
}
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ExitRepeatStmtNode */
struct ExitRepeatStmtNode : StmtNode {
explicit ExitRepeatStmtNode(uint32 offset) : StmtNode(kExitRepeatStmtNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* NextRepeatStmtNode */
struct NextRepeatStmtNode : StmtNode {
explicit NextRepeatStmtNode(uint32 offset) : StmtNode(kNextRepeatStmtNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* PutStmtNode */
struct PutStmtNode : StmtNode {
PutType type;
Common::SharedPtr<Node> variable;
Common::SharedPtr<Node> value;
PutStmtNode(uint32 offset, PutType t, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val)
: StmtNode(kPutStmtNode, offset), type(t) {
variable = Common::move(var);
variable->parent = this;
value = Common::move(val);
value->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* WhenStmtNode */
struct WhenStmtNode : StmtNode {
int event;
Common::String script;
WhenStmtNode(uint32 offset, int e, Common::String s)
: StmtNode(kWhenStmtNode, offset), event(e), script(s) {}
void accept(NodeVisitor &visitor) const override;
};
/* NewObjNode */
struct NewObjNode : ExprNode {
Common::String objType;
Common::SharedPtr<Node> objArgs;
NewObjNode(uint32 offset, Common::String o, Common::SharedPtr<Node> args) : ExprNode(kNewObjNode, offset), objType(o), objArgs(args) {}
void accept(NodeVisitor &visitor) const override;
};
class NodeVisitor {
public:
virtual ~NodeVisitor() {}
virtual void visit(const HandlerNode &node) { defaultVisit(node); }
virtual void visit(const ErrorNode &node) { defaultVisit(node); }
virtual void visit(const CommentNode &node) { defaultVisit(node); }
virtual void visit(const NewObjNode &node) { defaultVisit(node); }
virtual void visit(const LiteralNode &node) { defaultVisit(node); }
virtual void visit(const IfStmtNode &node) { defaultVisit(node); }
virtual void visit(const EndCaseNode &node) { defaultVisit(node); }
virtual void visit(const ObjCallNode &node) { defaultVisit(node); }
virtual void visit(const PutStmtNode &node) { defaultVisit(node); }
virtual void visit(const TheExprNode &node) { defaultVisit(node); }
virtual void visit(const BinaryOpNode &node) { defaultVisit(node); }
virtual void visit(const CaseStmtNode &node) { defaultVisit(node); }
virtual void visit(const ExitStmtNode &node) { defaultVisit(node); }
virtual void visit(const TellStmtNode &node) { defaultVisit(node); }
virtual void visit(const WhenStmtNode &node) { defaultVisit(node); }
virtual void visit(const CaseLabelNode &node) { defaultVisit(node); }
virtual void visit(const ChunkExprNode &node) { defaultVisit(node); }
virtual void visit(const InverseOpNode &node) { defaultVisit(node); }
virtual void visit(const ObjCallV4Node &node) { defaultVisit(node); }
virtual void visit(const OtherwiseNode &node) { defaultVisit(node); }
virtual void visit(const MemberExprNode &node) { defaultVisit(node); }
virtual void visit(const ObjPropExprNode &node) { defaultVisit(node); }
virtual void visit(const PlayCmdStmtNode &node) { defaultVisit(node); }
virtual void visit(const ThePropExprNode &node) { defaultVisit(node); }
virtual void visit(const MenuPropExprNode &node) { defaultVisit(node); }
virtual void visit(const SoundCmdStmtNode &node) { defaultVisit(node); }
virtual void visit(const SoundPropExprNode &node) { defaultVisit(node); }
virtual void visit(const AssignmentStmtNode &node) { defaultVisit(node); }
virtual void visit(const ExitRepeatStmtNode &node) { defaultVisit(node); }
virtual void visit(const NextRepeatStmtNode &node) { defaultVisit(node); }
virtual void visit(const ObjBracketExprNode &node) { defaultVisit(node); }
virtual void visit(const SpritePropExprNode &node) { defaultVisit(node); }
virtual void visit(const ChunkDeleteStmtNode &node) { defaultVisit(node); }
virtual void visit(const ChunkHiliteStmtNode &node) { defaultVisit(node); }
virtual void visit(const RepeatWhileStmtNode &node) { defaultVisit(node); }
virtual void visit(const MenuItemPropExprNode &node) { defaultVisit(node); }
virtual void visit(const ObjPropIndexExprNode &node) { defaultVisit(node); }
virtual void visit(const RepeatWithInStmtNode &node) { defaultVisit(node); }
virtual void visit(const RepeatWithToStmtNode &node) { defaultVisit(node); }
virtual void visit(const SpriteWithinExprNode &node) { defaultVisit(node); }
virtual void visit(const LastStringChunkExprNode &node) { defaultVisit(node); }
virtual void visit(const SpriteIntersectsExprNode &node) { defaultVisit(node); }
virtual void visit(const StringChunkCountExprNode &node) { defaultVisit(node); }
virtual void visit(const VarNode &node) { defaultVisit(node); }
virtual void visit(const CallNode &node) { defaultVisit(node); }
virtual void visit(const BlockNode &node) { defaultVisit(node); }
virtual void visit(const NotOpNode &node) { defaultVisit(node); }
virtual void defaultVisit(const Node &) {}
};
/* AST */
struct AST {
Common::SharedPtr<HandlerNode> root;
BlockNode *currentBlock;
AST(uint32 offset, Handler *handler){
root = Common::SharedPtr<HandlerNode>(new HandlerNode(offset, handler));
currentBlock = root->block.get();
}
void addStatement(Common::SharedPtr<Node> statement);
void enterBlock(BlockNode *block);
void exitBlock();
};
} // namespace LingoDec
#endif // LINGODEC_AST_H

View File

@@ -0,0 +1,876 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/ptr.h"
#include "./ast.h"
#include "./handler.h"
#include "./names.h"
#include "./script.h"
#include "./codewritervisitor.h"
namespace LingoDec {
void CodeWriterVisitor::visit(const HandlerNode &node) {
if (node.handler->isGenericEvent) {
node.block->accept(*this);
} else {
Script *script = node.handler->script;
bool isMethod = script->isFactory();
if (isMethod) {
write("method ");
} else {
write("on ");
}
write(node.handler->name);
if (node.handler->argumentNames.size() > 0) {
write(" ");
for (size_t i = 0; i < node.handler->argumentNames.size(); i++) {
if (i > 0)
write(", ");
write(node.handler->argumentNames[i]);
}
}
writeLine();
indent();
if (isMethod && script->propertyNames.size() > 0 && node.handler == &script->handlers[0]) {
write("instance ");
for (size_t i = 0; i < script->propertyNames.size(); i++) {
if (i > 0)
write(", ");
write(script->propertyNames[i]);
}
writeLine();
}
if (node.handler->globalNames.size() > 0) {
write("global ");
for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
if (i > 0)
write(", ");
write(node.handler->globalNames[i]);
}
writeLine();
}
unindent();
node.block->accept(*this);
if (!isMethod) {
writeLine("end");
}
}
}
void CodeWriterVisitor::visit(const ErrorNode &) {
write("ERROR");
}
void CodeWriterVisitor::visit(const CommentNode &node) {
write("-- ");
write(node.text);
}
void CodeWriterVisitor::visit(const NewObjNode &node) {
write("new ");
write(node.objType);
write("(");
node.objArgs->accept(*this);
write(")");
}
void CodeWriterVisitor::visit(const LiteralNode &node) {
write(*node.value);
}
void CodeWriterVisitor::visit(const IfStmtNode &node) {
write("if ");
node.condition->accept(*this);
write(" then");
if (_sum) {
if (node.hasElse) {
write(" / else");
}
} else {
writeLine();
node.block1->accept(*this);
if (node.hasElse) {
writeLine("else");
node.block2->accept(*this);
}
write("end if");
}
}
void CodeWriterVisitor::visit(const EndCaseNode &) {
write("end case");
}
void CodeWriterVisitor::visit(const ObjCallNode &node) {
auto &rawArgs = node.argList->getValue()->l;
auto &obj = rawArgs[0];
bool parenObj = obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
obj->accept(*this);
if (parenObj) {
write(")");
}
write(".");
write(node.name);
write("(");
for (size_t i = 1; i < rawArgs.size(); i++) {
if (i > 1)
write(", ");
rawArgs[i]->accept(*this);
}
write(")");
}
void CodeWriterVisitor::visit(const PutStmtNode &node) {
write("put ");
node.value->accept(*this);
write(" ");
write(StandardNames::putTypeNames[node.type]);
write(" ");
node.variable->accept(*this); // TODO: we want the variable to always be verbose
}
void CodeWriterVisitor::visit(const TheExprNode &node) {
write("the ");
write(node.prop);
}
void CodeWriterVisitor::visit(const BinaryOpNode &node) {
unsigned int precedence = node.getPrecedence();
bool parenLeft = false;
bool parenRight = false;
if (precedence) {
if (node.left->type == kBinaryOpNode) {
auto leftBinaryOpNode = static_cast<BinaryOpNode *>(node.left.get());
parenLeft = (leftBinaryOpNode->getPrecedence() != precedence);
}
parenRight = (node.right->type == kBinaryOpNode);
}
if (parenLeft) {
write("(");
}
node.left->accept(*this);
if (parenLeft) {
write(")");
}
write(" ");
write(StandardNames::binaryOpNames[node.opcode]);
write(" ");
if (parenRight) {
write("(");
}
node.right->accept(*this);
if (parenRight) {
write(")");
}
}
void CodeWriterVisitor::visit(const CaseStmtNode &node) {
write("case ");
node.value->accept(*this);
write(" of");
if (_sum) {
if (!node.firstLabel) {
if (node.otherwise) {
write(" / otherwise:");
} else {
write(" / end case");
}
}
} else {
writeLine();
indent();
if (node.firstLabel) {
node.firstLabel->accept(*this);
}
if (node.otherwise) {
node.otherwise->accept(*this);
}
unindent();
write("end case");
}
}
void CodeWriterVisitor::visit(const ExitStmtNode &) {
write("exit");
}
void CodeWriterVisitor::visit(const TellStmtNode &node) {
write("tell ");
node.window->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end tell");
}
}
void CodeWriterVisitor::visit(const WhenStmtNode &node) {
write("when ");
write(StandardNames::whenEventNames[node.event]);
write(" then");
for (size_t i = 0; i < node.script.size(); i++) {
char ch = node.script[i];
if (ch == '\r') {
if (i != node.script.size() - 1) {
writeLine();
}
} else {
write(ch);
}
}
}
void CodeWriterVisitor::visit(const CaseLabelNode &node) {
if (_sum) {
write("(case) ");
if (node.parent->type == kCaseLabelNode) {
auto parentLabel = static_cast<CaseLabelNode *>(node.parent);
if (parentLabel->nextOr.get() == &node) {
write("..., ");
}
}
bool parenValue = node.value->hasSpaces(_dot);
if (parenValue) {
write("(");
}
node.value->accept(*this);
if (parenValue) {
write(")");
}
if (node.nextOr) {
write(", ...");
} else {
write(":");
}
} else {
bool parenValue = node.value->hasSpaces(_dot);
if (parenValue) {
write("(");
}
node.value->accept(*this);
if (parenValue) {
write(")");
}
if (node.nextOr) {
write(", ");
node.nextOr->accept(*this);
} else {
writeLine(":");
node.block->accept(*this);
}
if (node.nextLabel) {
node.nextLabel->accept(*this);
}
}
}
void CodeWriterVisitor::visit(const ChunkExprNode &node) {
write(StandardNames::chunkTypeNames[node.type]);
write(" ");
node.first->accept(*this);
if (!(node.last->type == kLiteralNode && node.last->getValue()->type == kDatumInt && node.last->getValue()->i == 0)) {
write(" to ");
node.last->accept(*this);
}
write(" of ");
node.string->accept(*this); // TODO: we want the string to always be verbose
}
void CodeWriterVisitor::visit(const InverseOpNode &node) {
write("-");
bool parenOperand = node.operand->hasSpaces(_dot);
if (parenOperand) {
write("(");
}
node.operand->accept(*this);
if (parenOperand) {
write(")");
}
}
void CodeWriterVisitor::visit(const ObjCallV4Node &node) {
node.obj->accept(*this);
write("(");
node.argList->accept(*this);
write(")");
}
void CodeWriterVisitor::visit(const OtherwiseNode &node) {
if (_sum) {
write("(case) otherwise:");
} else {
writeLine("otherwise:");
node.block->accept(*this);
}
}
void CodeWriterVisitor::visit(const MemberExprNode &node) {
bool hasCastID = node.castID && !(node.castID->type == kLiteralNode && node.castID->getValue()->type == kDatumInt && node.castID->getValue()->i == 0);
write(node.type);
if (_dot) {
write("(");
node.memberID->accept(*this);
if (hasCastID) {
write(", ");
node.castID->accept(*this);
}
write(")");
} else {
write(" ");
bool parenMemberID = (node.memberID->type == kBinaryOpNode);
if (parenMemberID) {
write("(");
}
node.memberID->accept(*this);
if (parenMemberID) {
write(")");
}
if (hasCastID) {
write(" of castLib ");
bool parenCastID = (node.castID->type == kBinaryOpNode);
if (parenCastID) {
write("(");
}
node.castID->accept(*this);
if (parenCastID) {
write(")");
}
}
}
}
void CodeWriterVisitor::visit(const ObjPropExprNode &node) {
if (_dot) {
bool parenObj = node.obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
write(".");
write(node.prop);
} else {
write("the ");
write(node.prop);
write(" of ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
}
}
void CodeWriterVisitor::visit(const PlayCmdStmtNode &node) {
auto &rawArgs = node.argList->getValue()->l;
write("play");
if (rawArgs.size() == 0) {
write(" done");
return;
}
auto &frame = rawArgs[0];
if (rawArgs.size() == 1) {
write(" frame ");
frame->accept(*this);
return;
}
auto &movie = rawArgs[1];
if (!(frame->type == kLiteralNode && frame->getValue()->type == kDatumInt && frame->getValue()->i == 1)) {
write(" frame ");
frame->accept(*this);
write(" of");
}
write(" movie ");
movie->accept(*this);
}
void CodeWriterVisitor::visit(const ThePropExprNode &node) {
write("the ");
write(node.prop);
write(" of ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this); // TODO: we want the object to always be verbose
if (parenObj) {
write(")");
}
}
void CodeWriterVisitor::visit(const MenuPropExprNode &node) {
write("the ");
write(StandardNames::menuPropertyNames[node.prop]);
write(" of menu ");
bool parenMenuID = (node.menuID->type == kBinaryOpNode);
if (parenMenuID) {
write("(");
}
node.menuID->accept(*this);
if (parenMenuID) {
write(")");
}
}
void CodeWriterVisitor::visit(const SoundCmdStmtNode &node) {
write("sound ");
write(node.cmd);
if (node.argList->getValue()->l.size() > 0) {
write(" ");
node.argList->accept(*this);
}
}
void CodeWriterVisitor::visit(const SoundPropExprNode &node) {
write("the ");
write(StandardNames::soundPropertyNames[node.prop]);
write(" of sound ");
bool parenSoundID = (node.soundID->type == kBinaryOpNode);
if (parenSoundID) {
write("(");
}
node.soundID->accept(*this);
if (parenSoundID) {
write(")");
}
}
void CodeWriterVisitor::visit(const AssignmentStmtNode &node) {
if (!_dot) { // TODO: forceVerboseÒ
write("set ");
node.variable->accept(*this); // TODO: we want the variable to always be verbose
write(" to ");
node.value->accept(*this);
} else {
node.variable->accept(*this);
write(" = ");
node.value->accept(*this);
}
}
void CodeWriterVisitor::visit(const ExitRepeatStmtNode &) {
write("exit repeat");
}
void CodeWriterVisitor::visit(const NextRepeatStmtNode &) {
write("next repeat");
}
void CodeWriterVisitor::visit(const ObjBracketExprNode &node) {
bool parenObj = node.obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
write("[");
node.prop->accept(*this);
write("]");
}
void CodeWriterVisitor::visit(const SpritePropExprNode &node) {
write("the ");
write(StandardNames::spritePropertyNames[node.prop]);
write(" of sprite ");
bool parenSpriteID = (node.spriteID->type == kBinaryOpNode);
if (parenSpriteID) {
write("(");
}
node.spriteID->accept(*this);
if (parenSpriteID) {
write(")");
}
}
void CodeWriterVisitor::visit(const ChunkDeleteStmtNode &node) {
write("delete ");
node.chunk->accept(*this);
}
void CodeWriterVisitor::visit(const ChunkHiliteStmtNode &node) {
write("hilite ");
node.chunk->accept(*this);
}
void CodeWriterVisitor::visit(const RepeatWhileStmtNode &node) {
write("repeat while ");
node.condition->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end repeat");
}
}
void CodeWriterVisitor::visit(const MenuItemPropExprNode &node) {
write("the ");
write(StandardNames::menuItemPropertyNames[node.prop]);
write(" of menuItem ");
bool parenItemID = (node.itemID->type == kBinaryOpNode);
if (parenItemID) {
write("(");
}
node.itemID->accept(*this);
if (parenItemID) {
write(")");
}
write(" of menu ");
bool parenMenuID = (node.menuID->type ==kBinaryOpNode);
if (parenMenuID) {
write("(");
}
node.menuID->accept(*this);
if (parenMenuID) {
write(")");
}
}
void CodeWriterVisitor::visit(const ObjPropIndexExprNode &node) {
bool parenObj = node.obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
write(".");
write(node.prop);
write("[");
node.index->accept(*this);
if (node.index2) {
write("..");
node.index2->accept(*this);
}
write("]");
}
void CodeWriterVisitor::visit(const RepeatWithInStmtNode &node) {
write("repeat with ");
write(node.varName);
write(" in ");
node.list->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end repeat");
}
}
void CodeWriterVisitor::visit(const RepeatWithToStmtNode &node) {
write("repeat with ");
write(node.varName);
write(" = ");
node.start->accept(*this);
if (node.up) {
write(" to ");
} else {
write(" down to ");
}
node.end->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end repeat");
}
}
void CodeWriterVisitor::visit(const SpriteWithinExprNode &node) {
write("sprite ");
bool parenFirstSprite = (node.firstSprite->type == kBinaryOpNode);
if (parenFirstSprite) {
write("(");
}
node.firstSprite->accept(*this);
if (parenFirstSprite) {
write(")");
}
write(" within ");
bool parenSecondSprite = (node.secondSprite->type == kBinaryOpNode);
if (parenSecondSprite) {
write("(");
}
node.secondSprite->accept(*this);
if (parenSecondSprite) {
write(")");
}
}
void CodeWriterVisitor::visit(const LastStringChunkExprNode &node) {
write("the last ");
write(StandardNames::chunkTypeNames[node.type]);
write(" in ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this); // TODO: we want the object to always be verbose
if (parenObj) {
write(")");
}
}
void CodeWriterVisitor::visit(const SpriteIntersectsExprNode &node) {
write("sprite ");
bool parenFirstSprite = (node.firstSprite->type == kBinaryOpNode);
if (parenFirstSprite) {
write("(");
}
node.firstSprite->accept(*this);
if (parenFirstSprite) {
write(")");
}
write(" intersects ");
bool parenSecondSprite = (node.secondSprite->type == kBinaryOpNode);
if (parenSecondSprite) {
write("(");
}
node.secondSprite->accept(*this);
if (parenSecondSprite) {
write(")");
}
}
void CodeWriterVisitor::visit(const StringChunkCountExprNode &node) {
write("the number of ");
write(StandardNames::chunkTypeNames[node.type]); // we want the object to always be verbose
write("s in ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this); // TODO dot false?
if (parenObj) {
write(")");
}
}
void CodeWriterVisitor::visit(const VarNode &node) {
write(node.varName);
}
void CodeWriterVisitor::visit(const CallNode &node) {
if (node.isExpression && node.argList->getValue()->l.size() == 0) {
if (node.name == "pi") {
write("PI");
return;
}
if (node.name == "space") {
write("SPACE");
return;
}
if (node.name == "void") {
write("VOID");
return;
}
}
if (!_dot && node.isMemberExpr()) {
/**
* In some cases, member expressions such as `member 1 of castLib 1` compile
* to the function call `member(1, 1)`. However, this doesn't parse correctly
* in pre-dot-syntax versions of Director, and `put(member(1, 1))` does not
* compile. Therefore, we rewrite these expressions to the verbose syntax when
* in verbose mode.
*/
write(node.name);
write(" ");
auto memberID = node.argList->getValue()->l[0];
bool parenMemberID = (memberID->type == kBinaryOpNode);
if (parenMemberID) {
write("(");
}
memberID->accept(*this);
if (parenMemberID) {
write(")");
}
if (node.argList->getValue()->l.size() == 2) {
write(" of castLib ");
auto castID = node.argList->getValue()->l[1];
bool parenCastID = (castID->type == kBinaryOpNode);
if (parenCastID) {
write("(");
}
castID->accept(*this);
if (parenCastID) {
write(")");
}
}
return;
}
write(node.name);
if (node.noParens()) {
write(" ");
node.argList->accept(*this);
} else {
write("(");
node.argList->accept(*this);
write(")");
}
}
void CodeWriterVisitor::visit(const BlockNode &node) {
indent();
for (const auto &child : node.children) {
child->accept(*this);
writeLine();
}
unindent();
}
void CodeWriterVisitor::visit(const NotOpNode &node) {
write("not ");
bool parenOperand = node.operand->hasSpaces(_dot);
if (parenOperand) {
write("(");
}
node.operand->accept(*this);
if (parenOperand) {
write(")");
}
}
void CodeWriterVisitor::indent() {
_indent++;
}
void CodeWriterVisitor::unindent() {
if (_indent > 0)
_indent--;
}
void CodeWriterVisitor::writeIndentation() {
if (_indentWritten)
return;
for (int i = 0; i < _indent; i++) {
_str += _indentation;
}
_indentWritten = true;
_lineWidth = _indent * _indentation.size();
}
void CodeWriterVisitor::write(char c) {
writeIndentation();
_str += c;
_lineWidth++;
}
void CodeWriterVisitor::write(const Common::String &s) {
writeIndentation();
_str += s;
_lineWidth += s.size();
}
void CodeWriterVisitor::writeLine() {
_str += _lineEnding;
_lineWidth += _lineEnding.size();
_indentWritten = false;
_lineWidth = 0;
}
void CodeWriterVisitor::writeLine(const Common::String &s) {
writeIndentation();
_str += s;
_lineWidth += s.size();
_str += _lineEnding;
_lineWidth += _lineEnding.size();
_indentWritten = false;
_lineWidth = 0;
}
void CodeWriterVisitor::write(Datum &datum) {
switch (datum.type) {
case kDatumVoid:
write("VOID");
return;
case kDatumSymbol:
write("#" + datum.s);
return;
case kDatumVarRef:
write(datum.s);
return;
case kDatumString:
if (datum.s.size() == 0) {
write("EMPTY");
return;
}
if (datum.s.size() == 1) {
switch (datum.s[0]) {
case '\x03':
write("ENTER");
return;
case '\x08':
write("BACKSPACE");
return;
case '\t':
write("TAB");
return;
case '\r':
write("RETURN");
return;
case '"':
write("QUOTE");
return;
default:
break;
}
}
if (_sum) {
write("\"" + Common::toPrintable(datum.s) + "\"");
return;
}
write("\"" + datum.s + "\"");
return;
case kDatumInt:
write(Common::String::format("%d", datum.i));
return;
case kDatumFloat:
write(Common::String::format("%g", datum.f));
return;
case kDatumList:
case kDatumArgList:
case kDatumArgListNoRet: {
if (datum.type == kDatumList)
write("[");
for (size_t ii = 0; ii < datum.l.size(); ii++) {
if (ii > 0)
write(", ");
datum.l[ii]->accept(*this);
}
if (datum.type == kDatumList)
write("]");
}
return;
case kDatumPropList: {
write("[");
if (datum.l.size() == 0) {
write(":");
} else {
for (size_t ii = 0; ii < datum.l.size(); ii += 2) {
if (ii > 0)
write(", ");
datum.l[ii]->accept(*this);
write(": ");
datum.l[ii + 1]->accept(*this);
}
}
write("]");
}
return;
}
}
} // namespace LingoDec

View File

@@ -0,0 +1,92 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_CODEWRITERVISITOR_H
#define LINGODEC_CODEWRITERVISITOR_H
#include "./ast.h"
namespace LingoDec {
class CodeWriterVisitor: public LingoDec::NodeVisitor {
public:
CodeWriterVisitor(bool dotSyntax, bool sum, const Common::String &lineEnding = "\n", const Common::String &indentation = " ")
: _dot(dotSyntax), _sum(sum), _lineEnding(lineEnding), _indentation(indentation) {}
virtual ~CodeWriterVisitor() {}
virtual void visit(const LingoDec::HandlerNode& node) override;
virtual void visit(const LingoDec::ErrorNode& node) override;
virtual void visit(const LingoDec::CommentNode& node) override;
virtual void visit(const LingoDec::NewObjNode& node) override;
virtual void visit(const LingoDec::LiteralNode& node) override;
virtual void visit(const LingoDec::IfStmtNode& node) override;
virtual void visit(const LingoDec::EndCaseNode& node) override;
virtual void visit(const LingoDec::ObjCallNode& node) override;
virtual void visit(const LingoDec::PutStmtNode& node) override;
virtual void visit(const LingoDec::TheExprNode& node) override;
virtual void visit(const LingoDec::BinaryOpNode& node) override;
virtual void visit(const LingoDec::CaseStmtNode& node) override;
virtual void visit(const LingoDec::ExitStmtNode& node) override;
virtual void visit(const LingoDec::TellStmtNode& node) override;
virtual void visit(const LingoDec::WhenStmtNode& node) override;
virtual void visit(const LingoDec::CaseLabelNode& node) override;
virtual void visit(const LingoDec::ChunkExprNode& node) override;
virtual void visit(const LingoDec::InverseOpNode& node) override;
virtual void visit(const LingoDec::ObjCallV4Node& node) override;
virtual void visit(const LingoDec::OtherwiseNode& node) override;
virtual void visit(const LingoDec::MemberExprNode& node) override;
virtual void visit(const LingoDec::ObjPropExprNode& node) override;
virtual void visit(const LingoDec::PlayCmdStmtNode& node) override;
virtual void visit(const LingoDec::ThePropExprNode& node) override;
virtual void visit(const LingoDec::MenuPropExprNode& node) override;
virtual void visit(const LingoDec::SoundCmdStmtNode& node) override;
virtual void visit(const LingoDec::SoundPropExprNode& node) override;
virtual void visit(const LingoDec::AssignmentStmtNode& node) override;
virtual void visit(const LingoDec::ExitRepeatStmtNode& node) override;
virtual void visit(const LingoDec::NextRepeatStmtNode& node) override;
virtual void visit(const LingoDec::ObjBracketExprNode& node) override;
virtual void visit(const LingoDec::SpritePropExprNode& node) override;
virtual void visit(const LingoDec::ChunkDeleteStmtNode& node) override;
virtual void visit(const LingoDec::ChunkHiliteStmtNode& node) override;
virtual void visit(const LingoDec::RepeatWhileStmtNode& node) override;
virtual void visit(const LingoDec::MenuItemPropExprNode& node) override;
virtual void visit(const LingoDec::ObjPropIndexExprNode& node) override;
virtual void visit(const LingoDec::RepeatWithInStmtNode& node) override;
virtual void visit(const LingoDec::RepeatWithToStmtNode& node) override;
virtual void visit(const LingoDec::SpriteWithinExprNode& node) override;
virtual void visit(const LingoDec::LastStringChunkExprNode& node) override;
virtual void visit(const LingoDec::SpriteIntersectsExprNode& node) override;
virtual void visit(const LingoDec::StringChunkCountExprNode& node) override;
virtual void visit(const LingoDec::VarNode& node) override;
virtual void visit(const LingoDec::CallNode& node) override;
virtual void visit(const LingoDec::BlockNode& node) override;
virtual void visit(const LingoDec::NotOpNode& node) override;
size_t lineWidth() const { return _lineWidth; }
void indent();
void unindent();
void writeIndentation();
void write(char c);
void write(const Common::String& s);
void writeLine();
void writeLine(const Common::String& s);
void write(LingoDec::Datum& datum);
public:
Common::String _str;
private:
bool _dot = false;
bool _sum = false;
Common::String _lineEnding;
Common::String _indentation = " ";
bool _indentWritten = false;
int _indent = 0;
size_t _lineWidth = 0;
};
} // namespace LingoDec
#endif // LINGODEC_CODEWRITERVISITOR_H

View File

@@ -0,0 +1,84 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/stream.h"
#include "common/util.h"
#include "./context.h"
#include "./names.h"
#include "./resolver.h"
#include "./script.h"
namespace LingoDec {
struct ScriptContextMapEntry;
/* ScriptContext */
void ScriptContext::read(Common::SeekableReadStream &stream) {
// Lingo scripts are always big endian regardless of file endianness
unknown0 = stream.readSint32BE();
unknown1 = stream.readSint32BE();
entryCount = stream.readUint32BE();
entryCount2 = stream.readUint32BE();
entriesOffset = stream.readUint16BE();
unknown2 = stream.readSint16BE();
unknown3 = stream.readSint32BE();
unknown4 = stream.readSint32BE();
unknown5 = stream.readSint32BE();
lnamSectionID = stream.readSint32BE();
validCount = stream.readUint16BE();
flags = stream.readUint16BE();
freePointer = stream.readSint16BE();
stream.seek(entriesOffset);
sectionMap.resize(entryCount);
for (auto &entry : sectionMap) {
entry.read(stream);
}
lnam = resolver->getScriptNames(lnamSectionID);
for (uint32 i = 1; i <= entryCount; i++) {
auto section = sectionMap[i - 1];
if (section.sectionID > -1) {
Script *script = resolver->getScript(section.sectionID);
script->setContext(this);
scripts[i] = script;
}
}
for (auto it = scripts.begin(); it != scripts.end(); ++it) {
Script *script = it->second;
if (script->isFactory()) {
Script *parent = scripts[script->parentNumber + 1];
parent->factories.push_back(script);
}
}
}
bool ScriptContext::validName(int id) const {
return lnam->validName(id);
}
Common::String ScriptContext::getName(int id) const {
return lnam->getName(id);
}
void ScriptContext::parseScripts() {
for (auto it = scripts.begin(); it != scripts.end(); ++it) {
it->second->parse();
}
}
/* ScriptContextMapEntry */
void ScriptContextMapEntry::read(Common::SeekableReadStream &stream) {
unknown0 = stream.readSint32BE();
sectionID = stream.readSint32BE();
unknown1 = stream.readUint16BE();
unknown2 = stream.readUint16BE();
}
} // namespace LingoDec

View File

@@ -0,0 +1,70 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_CONTEXT_H
#define LINGODEC_CONTEXT_H
#include "common/array.h"
#include "common/stablemap.h"
namespace Common {
class SeekableReadStream;
}
namespace LingoDec {
class ChunkResolver;
struct Script;
struct ScriptContextMapEntry;
struct ScriptNames;
/* ScriptContext */
struct ScriptContext {
int32 unknown0;
int32 unknown1;
uint32 entryCount;
uint32 entryCount2;
uint16 entriesOffset;
int16 unknown2;
int32 unknown3;
int32 unknown4;
int32 unknown5;
int32 lnamSectionID;
uint16 validCount;
uint16 flags;
int16 freePointer;
unsigned int version;
ChunkResolver *resolver;
ScriptNames *lnam;
Common::Array<ScriptContextMapEntry> sectionMap;
Common::StableMap<uint32, Script *> scripts;
ScriptContext(unsigned int version_, ChunkResolver *resolver_) : version(version_),
resolver(resolver_),
lnam(nullptr) {}
void read(Common::SeekableReadStream &stream);
bool validName(int id) const;
Common::String getName(int id) const;
void parseScripts();
};
/* ScriptContextMapEntry */
struct ScriptContextMapEntry {
int32 unknown0;
int32 sectionID;
uint16 unknown1;
uint16 unknown2;
void read(Common::SeekableReadStream &stream);
};
} // namespace LingoDec
#endif // LINGODEC_CONTEXT_H

View File

@@ -0,0 +1,216 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_ENUMS_H
#define LINGODEC_ENUMS_H
namespace LingoDec {
enum OpCode {
// single-byte
kOpRet = 0x01,
kOpRetFactory = 0x02,
kOpPushZero = 0x03,
kOpMul = 0x04,
kOpAdd = 0x05,
kOpSub = 0x06,
kOpDiv = 0x07,
kOpMod = 0x08,
kOpInv = 0x09,
kOpJoinStr = 0x0a,
kOpJoinPadStr = 0x0b,
kOpLt = 0x0c,
kOpLtEq = 0x0d,
kOpNtEq = 0x0e,
kOpEq = 0x0f,
kOpGt = 0x10,
kOpGtEq = 0x11,
kOpAnd = 0x12,
kOpOr = 0x13,
kOpNot = 0x14,
kOpContainsStr = 0x15,
kOpContains0Str = 0x16,
kOpGetChunk = 0x17,
kOpHiliteChunk = 0x18,
kOpOntoSpr = 0x19,
kOpIntoSpr = 0x1a,
kOpGetField = 0x1b,
kOpStartTell = 0x1c,
kOpEndTell = 0x1d,
kOpPushList = 0x1e,
kOpPushPropList = 0x1f,
kOpSwap = 0x21,
// multi-byte
kOpPushInt8 = 0x41,
kOpPushArgListNoRet = 0x42,
kOpPushArgList = 0x43,
kOpPushCons = 0x44,
kOpPushSymb = 0x45,
kOpPushVarRef = 0x46,
kOpGetGlobal2 = 0x48,
kOpGetGlobal = 0x49,
kOpGetProp = 0x4a,
kOpGetParam = 0x4b,
kOpGetLocal = 0x4c,
kOpSetGlobal2 = 0x4e,
kOpSetGlobal = 0x4f,
kOpSetProp = 0x50,
kOpSetParam = 0x51,
kOpSetLocal = 0x52,
kOpJmp = 0x53,
kOpEndRepeat = 0x54,
kOpJmpIfZ = 0x55,
kOpLocalCall = 0x56,
kOpExtCall = 0x57,
kOpObjCallV4 = 0x58,
kOpPut = 0x59,
kOpPutChunk = 0x5a,
kOpDeleteChunk = 0x5b,
kOpGet = 0x5c,
kOpSet = 0x5d,
kOpGetMovieProp = 0x5f,
kOpSetMovieProp = 0x60,
kOpGetObjProp = 0x61,
kOpSetObjProp = 0x62,
kOpTellCall = 0x63,
kOpPeek = 0x64,
kOpPop = 0x65,
kOpTheBuiltin = 0x66,
kOpObjCall = 0x67,
kOpPushChunkVarRef = 0x6d,
kOpPushInt16 = 0x6e,
kOpPushInt32 = 0x6f,
kOpGetChainedProp = 0x70,
kOpPushFloat32 = 0x71,
kOpGetTopLevelProp = 0x72,
kOpNewObj = 0x73
};
enum DatumType {
kDatumVoid,
kDatumSymbol,
kDatumVarRef,
kDatumString,
kDatumInt,
kDatumFloat,
kDatumList,
kDatumArgList,
kDatumArgListNoRet,
kDatumPropList
};
enum ChunkExprType {
kChunkChar = 0x01,
kChunkWord = 0x02,
kChunkItem = 0x03,
kChunkLine = 0x04
};
enum PutType {
kPutInto = 0x01,
kPutAfter = 0x02,
kPutBefore = 0x03
};
enum NodeType {
kNoneNode,
kErrorNode,
kTempNode,
kCommentNode,
kLiteralNode,
kBlockNode,
kHandlerNode,
kExitStmtNode,
kInverseOpNode,
kNotOpNode,
kBinaryOpNode,
kChunkExprNode,
kChunkHiliteStmtNode,
kChunkDeleteStmtNode,
kSpriteIntersectsExprNode,
kSpriteWithinExprNode,
kMemberExprNode,
kVarNode,
kAssignmentStmtNode,
kIfStmtNode,
kRepeatWhileStmtNode,
kRepeatWithInStmtNode,
kRepeatWithToStmtNode,
kCaseStmtNode,
kCaseLabelNode,
kOtherwiseNode,
kEndCaseNode,
kTellStmtNode,
kSoundCmdStmtNode,
kPlayCmdStmtNode,
kCallNode,
kObjCallNode,
kObjCallV4Node,
kTheExprNode,
kLastStringChunkExprNode,
kStringChunkCountExprNode,
kMenuPropExprNode,
kMenuItemPropExprNode,
kSoundPropExprNode,
kSpritePropExprNode,
kThePropExprNode,
kObjPropExprNode,
kObjBracketExprNode,
kObjPropIndexExprNode,
kExitRepeatStmtNode,
kNextRepeatStmtNode,
kPutStmtNode,
kWhenStmtNode,
kNewObjNode
};
enum BytecodeTag {
kTagNone,
kTagSkip,
kTagRepeatWhile,
kTagRepeatWithIn,
kTagRepeatWithTo,
kTagRepeatWithDownTo,
kTagNextRepeatTarget,
kTagEndCase
};
enum CaseExpect {
kCaseExpectEnd,
kCaseExpectOr,
kCaseExpectNext,
kCaseExpectOtherwise
};
enum ScriptFlag {
kScriptFlagUnused = (1 << 0x0),
kScriptFlagFuncsGlobal = (1 << 0x1),
kScriptFlagVarsGlobal = (1 << 0x2), // Occurs in event scripts (which have no local vars). Correlated with use of alternate global var opcodes.
kScriptFlagUnk3 = (1 << 0x3),
kScriptFlagFactoryDef = (1 << 0x4),
kScriptFlagUnk5 = (1 << 0x5),
kScriptFlagUnk6 = (1 << 0x6),
kScriptFlagUnk7 = (1 << 0x7),
kScriptFlagHasFactory = (1 << 0x8),
kScriptFlagEventScript = (1 << 0x9),
kScriptFlagEventScript2 = (1 << 0xa),
kScriptFlagUnkB = (1 << 0xb),
kScriptFlagUnkC = (1 << 0xc),
kScriptFlagUnkD = (1 << 0xd),
kScriptFlagUnkE = (1 << 0xe),
kScriptFlagUnkF = (1 << 0xf)
};
enum LiteralType {
kLiteralString = 1,
kLiteralInt = 4,
kLiteralFloat = 9
};
}
#endif // LINGODEC_ENUMS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_HANDLER_H
#define LINGODEC_HANDLER_H
#include "common/array.h"
#include "common/stablemap.h"
#include "common/str.h"
#include "./enums.h"
namespace Common {
class SeekableReadStream;
}
namespace LingoDec {
struct AST;
struct Bytecode;
class CodeWriterVisitor;
struct Node;
struct Script;
/* Handler */
struct Handler {
int16 nameID = 0;
uint16 vectorPos = 0;
uint32 compiledLen = 0;
uint32 compiledOffset = 0;
uint16 argumentCount = 0;
uint32 argumentOffset = 0;
uint16 localsCount = 0;
uint32 localsOffset = 0;
uint16 globalsCount = 0;
uint32 globalsOffset = 0;
uint32 unknown1 = 0;
uint16 unknown2 = 0;
uint16 lineCount = 0;
uint32 lineOffset = 0;
uint32 stackHeight = 0;
Common::Array<int16> argumentNameIDs;
Common::Array<int16> localNameIDs;
Common::Array<int16> globalNameIDs;
Script *script = nullptr;
Common::Array<Bytecode> bytecodeArray;
Common::StableMap<uint32, size_t> bytecodePosMap;
Common::Array<Common::String> argumentNames;
Common::Array<Common::String> localNames;
Common::Array<Common::String> globalNames;
Common::String name;
Common::Array<Common::SharedPtr<Node>> stack;
AST ast;
bool isGenericEvent = false;
Handler(): ast(0, this) {}
void setScript(Script *s) {
script = s;
}
void readRecord(Common::SeekableReadStream &stream);
void readData(Common::SeekableReadStream &stream);
Common::Array<int16> readVarnamesTable(Common::SeekableReadStream &stream, uint16 count, uint32 offset);
void readNames();
bool validName(int id) const;
Common::String getName(int id) const;
Common::String getArgumentName(int id) const;
Common::String getLocalName(int id) const;
Common::SharedPtr<Node> pop();
int variableMultiplier();
Common::SharedPtr<Node> readVar(int varType);
Common::String getVarNameFromSet(const Bytecode &bytecode);
Common::SharedPtr<Node> readV4Property(uint32 offset, int propertyType, int propertyID);
Common::SharedPtr<Node> readChunkRef(uint32 offset, Common::SharedPtr<Node> string);
void tagLoops();
bool isRepeatWithIn(uint32 startIndex, uint32 endIndex);
BytecodeTag identifyLoop(uint32 startIndex, uint32 endIndex);
void parse();
uint32 translateBytecode(Bytecode &bytecode, uint32 index);
void writeBytecodeText(CodeWriterVisitor &code) const;
};
/* Bytecode */
struct Bytecode {
byte opID;
OpCode opcode;
int32 obj;
uint32 pos;
BytecodeTag tag;
uint32 ownerLoop;
Common::SharedPtr<Node> translation;
Bytecode(byte op, int32 o, uint32 p)
: opID(op), obj(o), pos(p), tag(kTagNone), ownerLoop(0xffffffff) {
opcode = static_cast<OpCode>(op >= 0x40 ? 0x40 + op % 0x40 : op);
}
};
}
#endif // LINGODEC_HANDLER_H

View File

@@ -0,0 +1,365 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/str.h"
#include "common/stream.h"
#include "common/util.h"
#include "./enums.h"
#include "./names.h"
namespace LingoDec {
namespace StandardNames {
/* StandardNames */
const char *const opcodeNamesS[] = {
// single-byte
"unk00",
"ret", // kOpRet 0x01
"retfactory", // kOpRetFactory 0x02
"pushzero", // kOpPushZero 0x03
"mul", // kOpMul 0x04
"add", // kOpAdd 0x05
"sub", // kOpSub 0x06
"div", // kOpDiv 0x07
"mod", // kOpMod 0x08
"inv", // kOpInv 0x09
"joinstr", // kOpJoinStr 0x0a
"joinpadstr", // kOpJoinPadStr 0x0b
"lt", // kOpLt 0x0c
"lteq", // kOpLtEq 0x0d
"nteq", // kOpNtEq 0x0e
"eq", // kOpEq 0x0f
"gt", // kOpGt 0x10
"gteq", // kOpGtEq 0x11
"and", // kOpAnd 0x12
"or", // kOpOr 0x13
"not", // kOpNot 0x14
"containsstr", // kOpContainsStr 0x15
"contains0str", // kOpContains0Str 0x16
"getchunk", // kOpGetChunk 0x17
"hilitechunk", // kOpHiliteChunk 0x18
"ontospr", // kOpOntoSpr 0x19
"intospr", // kOpIntoSpr 0x1a
"getfield", // kOpGetField 0x1b
"starttell", // kOpStartTell 0x1c
"endtell", // kOpEndTell 0x1d
"pushlist", // kOpPushList 0x1e
"pushproplist", // kOpPushPropList 0x1f
"unk20",
"swap", // kOpSwap 0x21
};
const char *const opcodeNamesM[] = {
// multi-byte
"unk40",
"pushint8", // kOpPushInt8 0x41
"pusharglistnoret", // kOpPushArgListNoRet 0x42
"pusharglist", // kOpPushArgList 0x43
"pushcons", // kOpPushCons 0x44
"pushsymb", // kOpPushSymb 0x45
"pushvarref", // kOpPushVarRef 0x46
"unk47",
"getglobal2", // kOpGetGlobal2 0x48
"getglobal", // kOpGetGlobal 0x49
"getprop", // kOpGetProp 0x4a
"getparam", // kOpGetParam 0x4b
"getlocal", // kOpGetLocal 0x4c
"unk4d",
"setglobal2", // kOpSetGlobal2 0x4e
"setglobal", // kOpSetGlobal 0x4f
"setprop", // kOpSetProp 0x50
"setparam", // kOpSetParam 0x51
"setlocal", // kOpSetLocal 0x52
"jmp", // kOpJmp 0x53
"endrepeat", // kOpEndRepeat 0x54
"jmpifz", // kOpJmpIfZ 0x55
"localcall", // kOpLocalCall 0x56
"extcall", // kOpExtCall 0x57
"objcallv4", // kOpObjCallV4 0x58
"put", // kOpPut 0x59
"putchunk", // kOpPutChunk 0x5a
"deletechunk", // kOpDeleteChunk 0x5b
"get", // kOpGet 0x5c
"set", // kOpSet 0x5d
"unk5e",
"getmovieprop", // kOpGetMovieProp 0x5f
"setmovieprop", // kOpSetMovieProp 0x60
"getobjprop", // kOpGetObjProp 0x61
"setobjprop", // kOpSetObjProp 0x62
"tellcall", // kOpTellCall 0x63
"peek", // kOpPeek 0x64
"pop", // kOpPop 0x65
"thebuiltin", // kOpTheBuiltin 0x66
"objcall", // kOpObjCall 0x67
"unk68",
"unk69",
"unk6a",
"unk6b",
"unk6c",
"pushchunkvarref", // kOpPushChunkVarRef 0x6d
"pushint16", // kOpPushInt16 0x6e
"pushint32", // kOpPushInt32 0x6f
"getchainedprop", // kOpGetChainedProp 0x70
"pushfloat32", // kOpPushFloat32 0x71
"gettoplevelprop", // kOpGetTopLevelProp 0x72
"newobj", // kOpNewObj 0x73
};
const char *const binaryOpNames[] = {
"unk00",
"unk01",
"unk02",
"unk03",
"*", // kOpMul 0x04
"+", // kOpAdd 0x05
"-", // kOpSub 0x06
"/", // kOpDiv 0x07
"mod", // kOpMod 0x08
"unk09",
"&", // kOpJoinStr 0x0a
"&&", // kOpJoinPadStr 0x0b
"<", // kOpLt 0x0c
"<=", // kOpLtEq 0x0d
"<>", // kOpNtEq 0x0e
"=", // kOpEq 0x0f
">", // kOpGt 0x10
">=", // kOpGtEq 0x11
"and", // kOpAnd 0x12
"or", // kOpOr 0x13
"unk14",
"contains", // kOpContainsStr 0x15
"starts", // kOpContains0Str 0x16
};
const char *const chunkTypeNames[] = {
"unk00",
"char", // kChunkChar 0x01
"word", // kChunkWord 0x02
"item", // kChunkItem 0x03
"line", // kChunkLine 0x04
};
const char *const putTypeNames[] = {
"unk00",
"into", // kPutInto 0x01
"after", // kPutAfter 0x02
"before", // kPutBefore 0x03
};
const char *const moviePropertyNames[] = {
"floatPrecision", // 0x00
"mouseDownScript", // 0x01
"mouseUpScript", // 0x02
"keyDownScript", // 0x03
"keyUpScript", // 0x04
"timeoutScript", // 0x05
"short time", // 0x06
"abbr time", // 0x07
"long time", // 0x08
"short date", // 0x09
"abbr date", // 0x0a
"long date", // 0x0b
};
const char *const whenEventNames[] = {
"unk00",
"mouseDown",// 0x01
"mouseUp", // 0x02
"keyDown", // 0x03
"keyUp", // 0x04
"timeOut", // 0x05
};
const char *const menuPropertyNames[] = {
"unk00",
"name", // 0x01
"number of menuItems", // 0x02
};
const char *const menuItemPropertyNames[] = {
"unk00",
"name", // 0x01
"checkMark",// 0x02
"enabled", // 0x03
"script", // 0x04
};
const char *const soundPropertyNames[] = {
"unk00",
"volume", // 0x01
};
const char *const spritePropertyNames[] = {
"unk00",
"type", // 0x01
"backColor", // 0x02
"bottom", // 0x03
"castNum", // 0x04
"constraint", // 0x05
"cursor", // 0x06
"foreColor", // 0x07
"height", // 0x08
"immediate", // 0x09
"ink", // 0x0a
"left", // 0x0b
"lineSize", // 0x0c
"locH", // 0x0d
"locV", // 0x0e
"movieRate", // 0x0f
"movieTime", // 0x10
"pattern", // 0x11
"puppet", // 0x12
"right", // 0x13
"startTime", // 0x14
"stopTime", // 0x15
"stretch", // 0x16
"top", // 0x17
"trails", // 0x18
"visible", // 0x19
"volume", // 0x1a
"width", // 0x1b
"blend", // 0x1c
"scriptNum", // 0x1d
"moveableSprite", // 0x1e
"editableText", // 0x1f
"scoreColor", // 0x20
"loc", // 0x21
"rect", // 0x22
"memberNum", // 0x23
"castLibNum", // 0x24
"member", // 0x25
"scriptInstanceList", // 0x26
"currentTime", // 0x27
"mostRecentCuePoint", // 0x28
"tweened", // 0x29
"name", // 0x2a
};
const char *const animationPropertyNames[] = {
"unk00",
"beepOn", // 0x01
"buttonStyle", // 0x02
"centerStage", // 0x03
"checkBoxAccess", // 0x04
"checkboxType", // 0x05
"colorDepth", // 0x06
"colorQD", // 0x07
"exitLock", // 0x08
"fixStageSize", // 0x09
"fullColorPermit", // 0x0a
"imageDirect", // 0x0b
"doubleClick", // 0x0c
"key", // 0x0d
"lastClick", // 0x0e
"lastEvent", // 0x0f
"keyCode", // 0x10
"lastKey", // 0x11
"lastRoll", // 0x12
"timeoutLapsed", // 0x13
"multiSound", // 0x14
"pauseState", // 0x15
"quickTimePresent", // 0x16
"selEnd", // 0x17
"selStart", // 0x18
"soundEnabled", // 0x19
"soundLevel", // 0x1a
"stageColor", // 0x1b
// 0x1c indicates dontPassEvent was called.
// It doesn't seem to have a Lingo-accessible name.
"unk1c",
"switchColorDepth", // 0x1d
"timeoutKeyDown", // 0x1e
"timeoutLength", // 0x1f
"timeoutMouse", // 0x20
"timeoutPlay", // 0x21
"timer", // 0x22
"preLoadRAM", // 0x23
"videoForWindowsPresent", // 0x24
"netPresent", // 0x25
"safePlayer", // 0x26
"soundKeepDevice", // 0x27
"soundMixMedia", // 0x28
};
const char *const animation2PropertyNames[] = {
"unk00",
"perFrameHook", // 0x01
"number of castMembers",// 0x02
"number of menus", // 0x03
"number of castLibs", // 0x04
"number of xtras", // 0x05
};
const char *const memberPropertyNames[] = {
"unk00",
"name", // 0x01
"text", // 0x02
"textStyle", // 0x03
"textFont", // 0x04
"textHeight", // 0x05
"textAlign", // 0x06
"textSize", // 0x07
"picture", // 0x08
"hilite", // 0x09
"number", // 0x0a
"size", // 0x0b
"loop", // 0x0c
"duration", // 0x0d
"controller", // 0x0e
"directToStage",// 0x0f
"sound", // 0x10
"foreColor", // 0x11
"backColor", // 0x12
"type", // 0x13
};
Common::String getOpcodeName(byte id) {
if (id < 0x22)
return opcodeNamesS[id];
if (id < 0x40)
return Common::String::format("unk%02x" , id);
id = id % 0x40;
if (id < 0x34)
return opcodeNamesM[id];
return Common::String::format("unk%02x" , id);
}
} // namespace StandardNames
/* ScriptNames */
void ScriptNames::read(Common::SeekableReadStream &stream) {
// Lingo scripts are always big endian regardless of file endianness
unknown0 = stream.readSint32BE();
unknown1 = stream.readSint32BE();
len1 = stream.readUint32BE();
len2 = stream.readUint32BE();
namesOffset = stream.readUint16BE();
namesCount = stream.readUint16BE();
stream.seek(namesOffset);
names.resize(namesCount);
for (auto &name : names) {
name = stream.readPascalString();
}
}
bool ScriptNames::validName(int id) const {
return -1 < id && (unsigned)id < names.size();
}
Common::String ScriptNames::getName(int id) const {
if (validName(id))
return names[id];
return Common::String::format("UNKNOWN_NAME_%d", id);
}
}

View File

@@ -0,0 +1,63 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_NAMES_H
#define LINGODEC_NAMES_H
#include "common/array.h"
#include "common/stablemap.h"
namespace Common {
class SeekableReadStream;
class String;
}
namespace LingoDec {
/* StandardNames */
namespace StandardNames {
extern const char *const opcodeNamesS[];
extern const char *const opcodeNamesM[];
extern const char *const binaryOpNames[];
extern const char *const chunkTypeNames[];
extern const char *const putTypeNames[];
extern const char *const moviePropertyNames[];
extern const char *const whenEventNames[];
extern const char *const menuPropertyNames[];
extern const char *const menuItemPropertyNames[];
extern const char *const soundPropertyNames[];
extern const char *const spritePropertyNames[];
extern const char *const animationPropertyNames[];
extern const char *const animation2PropertyNames[];
extern const char *const memberPropertyNames[];
Common::String getOpcodeName(byte id);
}
/* ScriptNames */
struct ScriptNames {
int32 unknown0 = 0;
int32 unknown1 = 0;
uint32 len1 = 0;
uint32 len2 = 0;
uint16 namesOffset = 0;
uint16 namesCount = 0;
Common::Array<Common::String> names;
unsigned int version;
ScriptNames(unsigned int version_) : version(version_) {}
void read(Common::SeekableReadStream &stream);
bool validName(int id) const;
Common::String getName(int id) const;
};
} // namespace LingoDec
#endif // LINGODEC_NAMES_H

View File

@@ -0,0 +1,25 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_RESOLVER_H
#define LINGODEC_RESOLVER_H
namespace LingoDec {
struct Script;
struct ScriptNames;
class ChunkResolver {
public:
ChunkResolver() {}
virtual ~ChunkResolver() {}
virtual Script *getScript(int32 id) = 0;
virtual ScriptNames *getScriptNames(int32 id) = 0;
};
}
#endif // LINGODEC_RESOLVER_H

View File

@@ -0,0 +1,256 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/stream.h"
#include "./ast.h"
#include "./codewritervisitor.h"
#include "./context.h"
#include "./handler.h"
#include "./script.h"
double readAppleFloat80(void *ptr);
namespace LingoDec {
/* Script */
Script::Script(unsigned int version_) :
version(version_),
context(nullptr) {}
Script::~Script() = default;
void Script::read(Common::SeekableReadStream &stream) {
// Lingo scripts are always big endian regardless of file endianness
stream.seek(8);
/* 8 */ totalLength = stream.readUint32BE();
/* 12 */ totalLength2 = stream.readUint32BE();
/* 16 */ headerLength = stream.readUint16BE();
/* 18 */ scriptNumber = stream.readUint16BE();
/* 20 */ unk20 = stream.readSint16BE();
/* 22 */ parentNumber = stream.readSint16BE();
stream.seek(38);
/* 38 */ scriptFlags = stream.readUint32BE();
/* 42 */ unk42 = stream.readSint16BE();
/* 44 */ unk43 = stream.readSint16BE();
/* 46 */ castID = stream.readSint16BE();
/* 48 */ factoryNameID = stream.readSint16BE();
/* 50 */ handlerVectorsCount = stream.readUint16BE();
/* 52 */ handlerVectorsOffset = stream.readUint32BE();
/* 56 */ handlerVectorsSize = stream.readUint32BE();
/* 60 */ propertiesCount = stream.readUint16BE();
/* 62 */ propertiesOffset = stream.readUint32BE();
/* 66 */ globalsCount = stream.readUint16BE();
/* 68 */ globalsOffset = stream.readUint32BE();
/* 72 */ handlersCount = stream.readUint16BE();
/* 74 */ handlersOffset = stream.readUint32BE();
/* 78 */ literalsCount = stream.readUint16BE();
/* 80 */ literalsOffset = stream.readUint32BE();
/* 84 */ literalsDataCount = stream.readUint32BE();
/* 88 */ literalsDataOffset = stream.readUint32BE();
propertyNameIDs = readVarnamesTable(stream, propertiesCount, propertiesOffset);
globalNameIDs = readVarnamesTable(stream, globalsCount, globalsOffset);
handlers.resize(handlersCount);
for (auto &handler : handlers) {
handler.setScript(this);
}
if ((scriptFlags & LingoDec::kScriptFlagEventScript) && handlersCount > 0) {
handlers[0].isGenericEvent = true;
}
stream.seek(handlersOffset);
for (auto &handler : handlers) {
handler.readRecord(stream);
}
for (auto &handler : handlers) {
handler.readData(stream);
}
stream.seek(literalsOffset);
literals.resize(literalsCount);
for (auto &literal : literals) {
literal.readRecord(stream, version);
}
for (auto &literal : literals) {
literal.readData(stream, literalsDataOffset);
}
}
Common::Array<int16> Script::readVarnamesTable(Common::SeekableReadStream &stream, uint16 count, uint32 offset) {
stream.seek(offset);
Common::Array<int16> nameIDs(count);
for (uint16 i = 0; i < count; i++) {
nameIDs[i] = stream.readSint16BE();
}
return nameIDs;
}
bool Script::validName(int id) const {
return context->validName(id);
}
Common::String Script::getName(int id) const {
return context->getName(id);
}
void Script::setContext(ScriptContext *ctx) {
this->context = ctx;
if (factoryNameID != -1) {
factoryName = getName(factoryNameID);
}
for (auto nameID : propertyNameIDs) {
if (validName(nameID)) {
Common::String name = getName(nameID);
if (isFactory() && name == "me")
continue;
propertyNames.push_back(name);
}
}
for (auto nameID : globalNameIDs) {
if (validName(nameID)) {
globalNames.push_back(getName(nameID));
}
}
for (auto &handler : handlers) {
handler.readNames();
}
}
void Script::parse() {
for (auto &handler : handlers) {
handler.parse();
}
}
void Script::writeVarDeclarations(CodeWriterVisitor &code) const {
if (!isFactory()) {
if (propertyNames.size() > 0) {
code.write("property ");
for (size_t i = 0; i < propertyNames.size(); i++) {
if (i > 0)
code.write(", ");
code.write(propertyNames[i]);
}
code.writeLine();
}
}
if (globalNames.size() > 0) {
code.write("global ");
for (size_t i = 0; i < globalNames.size(); i++) {
if (i > 0)
code.write(", ");
code.write(globalNames[i]);
}
code.writeLine();
}
}
void Script::writeScriptText(CodeWriterVisitor &code) const {
size_t origSize = code._str.size();
writeVarDeclarations(code);
if (isFactory()) {
if (code._str.size() != origSize) {
code.writeLine();
}
code.write("factory ");
code.writeLine(factoryName);
}
for (size_t i = 0; i < handlers.size(); i++) {
if ((!isFactory() || i > 0) && code._str.size() != origSize) {
code.writeLine();
}
handlers[i].ast.root->accept(code);
}
for (auto factory : factories) {
if (code._str.size() != origSize) {
code.writeLine();
}
factory->writeScriptText(code);
}
}
Common::String Script::scriptText(const char *lineEnding, bool dotSyntax) const {
CodeWriterVisitor code(dotSyntax, false, lineEnding);
writeScriptText(code);
return code._str;
}
void Script::writeBytecodeText(CodeWriterVisitor &code) const {
size_t origSize = code._str.size();
writeVarDeclarations(code);
if (isFactory()) {
if (code._str.size() != origSize) {
code.writeLine();
}
code.write("factory ");
code.writeLine(factoryName);
}
for (size_t i = 0; i < handlers.size(); i++) {
if ((!isFactory() || i > 0) && code._str.size() != origSize) {
code.writeLine();
}
handlers[i].writeBytecodeText(code);
}
for (auto factory : factories) {
if (code._str.size() != origSize) {
code.writeLine();
}
factory->writeBytecodeText(code);
}
}
Common::String Script::bytecodeText(const char *lineEnding, bool dotSyntax) const {
CodeWriterVisitor code(dotSyntax, true, lineEnding);
writeBytecodeText(code);
return code._str;
}
bool Script::isFactory() const {
return (scriptFlags & LingoDec::kScriptFlagFactoryDef);
}
/* LiteralStore */
void LiteralStore::readRecord(Common::SeekableReadStream &stream, int version) {
if (version >= 500)
type = static_cast<LiteralType>(stream.readUint32BE());
else
type = static_cast<LiteralType>(stream.readUint16BE());
offset = stream.readUint32BE();
}
void LiteralStore::readData(Common::SeekableReadStream &stream, uint32 startOffset) {
if (type == kLiteralInt) {
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum((int)offset));
} else {
stream.seek(startOffset + offset);
auto length = stream.readUint32BE();
if (type == kLiteralString) {
char *buf = new char[length];
stream.read(buf, length - 1);
buf[length - 1] = '\0';
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum(LingoDec::kDatumString, Common::String(buf)));
delete[] buf;
} else if (type == kLiteralFloat) {
double floatVal = 0.0;
if (length == 8) {
floatVal = stream.readDoubleBE();
} else if (length == 10) {
byte buf[10];
stream.read(buf, 10);
floatVal = readAppleFloat80(buf);
}
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum(floatVal));
} else {
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum());
}
}
}
} // namespace LingoDec

View File

@@ -0,0 +1,98 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_SCRIPT_H
#define LINGODEC_SCRIPT_H
#include "common/ptr.h"
#include "common/str.h"
#include "./enums.h"
namespace Common {
class ReadStream;
}
namespace LingoDec {
class CodeWriterVisitor;
struct Datum;
struct Handler;
struct ScriptContext;
/* LiteralStore */
struct LiteralStore {
LiteralType type;
uint32 offset;
Common::SharedPtr<Datum> value;
void readRecord(Common::SeekableReadStream &stream, int version);
void readData(Common::SeekableReadStream &stream, uint32 startOffset);
};
/* Script */
struct Script {
/* 8 */ uint32 totalLength;
/* 12 */ uint32 totalLength2;
/* 16 */ uint16 headerLength;
/* 18 */ uint16 scriptNumber;
/* 20 */ int16 unk20;
/* 22 */ int16 parentNumber;
/* 38 */ uint32 scriptFlags;
/* 42 */ int16 unk42;
/* 44 */ int16 unk43;
// This castID is not reliable
/* 46 */ int16 castID;
/* 48 */ int16 factoryNameID;
/* 50 */ uint16 handlerVectorsCount;
/* 52 */ uint32 handlerVectorsOffset;
/* 56 */ uint32 handlerVectorsSize;
/* 60 */ uint16 propertiesCount;
/* 62 */ uint32 propertiesOffset;
/* 66 */ uint16 globalsCount;
/* 68 */ uint32 globalsOffset;
/* 72 */ uint16 handlersCount;
/* 74 */ uint32 handlersOffset;
/* 78 */ uint16 literalsCount;
/* 80 */ uint32 literalsOffset;
/* 84 */ uint32 literalsDataCount;
/* 88 */ uint32 literalsDataOffset;
Common::Array<int16> propertyNameIDs;
Common::Array<int16> globalNameIDs;
Common::String factoryName;
Common::Array<Common::String> propertyNames;
Common::Array<Common::String> globalNames;
Common::Array<Handler> handlers;
Common::Array<LiteralStore> literals;
Common::Array<Script *> factories;
unsigned int version;
ScriptContext *context;
Script(unsigned int version_);
~Script();
void read(Common::SeekableReadStream &stream);
Common::Array<int16> readVarnamesTable(Common::SeekableReadStream &stream, uint16 count, uint32 offset);
bool validName(int id) const;
Common::String getName(int id) const;
void setContext(ScriptContext *ctx);
void parse();
void writeVarDeclarations(CodeWriterVisitor &code) const;
void writeScriptText(CodeWriterVisitor &code) const;
Common::String scriptText(const char *lineEnding, bool dotSyntax) const;
void writeBytecodeText(CodeWriterVisitor &code) const;
Common::String bytecodeText(const char *lineEnding, bool dotSyntax) const;
bool isFactory() const;
};
} // namespace LingoDec
#endif // LINGODEC_SCRIPT_H

View File

@@ -0,0 +1,37 @@
openXLib("FlushXObj")
set flush = FlushXObj(mNew)
flush(mClearMask)
flush(mAddToMask, 0, 0)
flush(mFlush)
flush(mFlushEvents, 0, 0)
openXLib("PalXObj")
set fixpal = FixPalette(mNew, 0, 20, 512, 20)
fixpal(mPatchIt)
openXLib("winXOBJ")
set winxobj = RearWindow(mNew, "M")
scummVMAssert(winxobj(mGetMemoryNeeded) = 0)
-- test closing XObject
scummVMAssert(objectp(FlushXObj) = 1)
closeXlib "FlushXObj"
scummVMAssert(objectp(FlushXObj) = 0)
scummVMAssert(objectp(RearWindow) = 1)
closeXlib()
scummVMAssert(objectp(RearWindow) = 0)
-- test showGlobals and clearGlobals on XObjects
clearGlobals()
openXLib("FileIO")
scummVMAssert(objectp(FileIO))
showGlobals() -- FileIO should not be listed
clearGlobals()
scummVMAssert(objectp(FileIO)) -- FileIO should not be cleared
set FileIO = "test"
showGlobals() -- FileIO should be listed
clearGlobals()
scummVMAssert(voidp(FileIO)) -- FileIO should be cleared

Binary file not shown.

View File

@@ -0,0 +1,55 @@
beep
beep 3
alert "Hi"
alert "Hi" && "there"
open "Hello"
open "Hello" with "Finder"
open "Hello" && "more" with "Finder"
-- These are D4+
move cast 1, cast 1
move cast 1, 3
move cast 3, 1
move cast 1
put findEmpty(cast 10)
pasteClipBoardInto cast 2
put the width of cast 1
-- Puppet commands must come after the move commands
-- move does a force render. Combined with puppetTransitions it just waits and causes a timeout on the buildbot
puppetPalette "Rainbow"
puppetPalette "System",30
puppetPalette "custompal", 15,4
puppetSound "Air Lock Lock"
puppetSound 0
puppetSprite 15, true
puppetTempo 30
puppetTransition 1
puppetTransition 2,4,20
unload
unload ab
unload ab, bc
put framesToHMS(integer(field 3),30,false,false) into field 5
set save to the scummvmVersion
set the scummvmVersion to 300
scummvmAssert(version = "3.0")
set the scummvmVersion to 310
scummvmAssert(version = "3.1")
set the scummvmVersion to 404
scummvmAssert(version = "4.0.4")
set the scummvmVersion to 1201
scummvmAssert(version = "12.0.1")
set the scummvmVersion to save
set n = getNthFileNameInFolder("@", 1)
scummvmAssertEqual(n, "0testfile")

View File

@@ -0,0 +1,23 @@
-- This file is binary. Watch for 0xC2 symbol at the line ends
if the text of cast 1 <> the text of cast 2 then Â
put "Hello"
if the text of cast 1 <> the text of cast 2 then Â
put "Goodbye"
set the selStart to Â
0
set the selStart to Â
0
-- This comment is Â
the continuationÂ
of previous line
if the castNum of sprite 1 > the number of cast 2 then set the castNum of sprite 1 to the number of Â
cast 1
put "HelloÂ
World!"

View File

@@ -0,0 +1,45 @@
-- put randomName() into item i of field "In"
-- put the last word of field 1 into field 3
scummvmAssertEqual(the last char of "Macromedia, the multimedia company", "y")
scummvmAssertEqual(the last word of "Macromedia, the multimedia company", "company")
scummvmAssertEqual(the last word of "Macromedia, the multimedia company" && "man", "company man")
scummvmAssertEqual(word -30000 of "Macromedia, the multimedia company", "company")
scummvmAssertEqual(word -30000 of "Macromedia, the multimedia company" && "man", "company man")
set src = "abcdefghijklmnopqrstuvwxyz"
scummvmAssertEqual(char 2 of src, "b")
scummvmAssertEqual(char 2 to 0 of src, "b")
scummvmAssertEqual(char 2 to 4 of src, "bcd")
scummvmAssertEqual(char 2 to 1000 of src, "bcdefghijklmnopqrstuvwxyz")
scummvmAssertEqual(char 1000 of src, "")
set src = " the quick brown fox jumped over the lazy dog"
scummvmAssertEqual(word 2 of src, "quick")
scummvmAssertEqual(word 2 to 4 of src, "quick brown fox")
scummvmAssertEqual(word 2 to 1000 of src, "quick brown fox jumped over the lazy dog")
scummvmAssertEqual(word 1000 of src, "")
set src = "the,quick,brown,fox,jumped,over,the,lazy,dog"
scummvmAssertEqual(item 2 of src, "quick")
scummvmAssertEqual(item 2 to 4 of src, "quick,brown,fox")
scummvmAssertEqual(item 2 to 1000 of src, "quick,brown,fox,jumped,over,the,lazy,dog")
scummvmAssertEqual(item 1000 of src, "")
set src = "the quick" & RETURN & "brown fox" & RETURN & "jumped over" & RETURN & "the lazy dog"
scummvmAssertEqual(line 2 of src, "brown fox")
scummvmAssertEqual(line 2 to 4 of src, "brown fox" & RETURN & "jumped over" & RETURN & "the lazy dog")
scummvmAssertEqual(line 2 to 1000 of src, "brown fox" & RETURN & "jumped over" & RETURN & "the lazy dog")
scummvmAssertEqual(line 1000 of src, "")
put "abcdefghijklmnopqrstuvwxyz" into field 1
scummvmAssertEqual(char 2 of field 1, "b")
scummvmAssertEqual(char 2 to 0 of field 1, "b")
scummvmAssertEqual(char 2 to 4 of field 1, "bcd")
scummvmAssertEqual(char 2 to 1000 of field 1, "bcdefghijklmnopqrstuvwxyz")
scummvmAssertEqual(char 1000 of field 1, "")
put "lorem ipsum dolor sit amet" into field 1
set the foreColor of word 2 of field 1 to 10
put the foreColor of word 2 of field 1

View File

@@ -0,0 +1,93 @@
put "qwertyuiop" into test
delete char 2 of test
scummvmAssertEqual(test, "qertyuiop")
put "qwertyuiop" into test
delete char 2 to 9 of test
scummvmAssertEqual(test, "qp")
put "qwertyuiop" into test
delete char 2 to 1000 of test
scummvmAssertEqual(test, "q")
put "qwertyuiop" into test
delete char 0 of test
scummvmAssertEqual(test, "")
put "qwertyuiop" into test
delete the last char of test
scummvmAssertEqual(test, "qwertyuio")
put "lorem ipsum dolor" into test
delete word 2 of test
scummvmAssertEqual(test, "lorem dolor")
put "lorem ipsum dolor" into test
delete word 2 to 3 of test
scummvmAssertEqual(test, "lorem ")
put "lorem ipsum dolor" into test
delete word 2 to 1000 of test
scummvmAssertEqual(test, "lorem ")
put "lorem ipsum dolor" into test
delete the last word of test
scummvmAssertEqual(test, "lorem ipsum ")
put "lorem,ipsum,dolor,sit,amet" into test
delete item 3 of test
scummvmAssertEqual(test, "lorem,ipsum,sit,amet")
put "lorem,ipsum,dolor,sit,amet" into test
delete item 2 to 5 of test
scummvmAssertEqual(test, "lorem")
put "lorem,ipsum,dolor,sit,amet" into test
delete item 1 of test
scummvmAssertEqual(test, "ipsum,dolor,sit,amet")
put "lorem,ipsum,dolor,sit,amet" into test
delete the last item of test
scummvmAssertEqual(test, "lorem,ipsum,dolor,sit")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor" & RETURN & "sit" & RETURN & "amet" into test
delete line 3 of test
scummvmAssertEqual(test, "lorem" & RETURN & "ipsum" & RETURN & "sit" & RETURN & "amet")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor" & RETURN & "sit" & RETURN & "amet" into test
delete line 2 to 5 of test
scummvmAssertEqual(test, "lorem")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor" & RETURN & "sit" & RETURN & "amet" into test
delete the last line of test
scummvmAssertEqual(test, "lorem" & RETURN & "ipsum" & RETURN & "dolor" & RETURN & "sit")
put "foo" & RETURN & "lorem ipsum" into test
delete char 3 of word 2 of line 2 of test
scummvmAssertEqual(test, "foo" & RETURN & "lorem ipum")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor,sit,amet" & RETURN & "nunc" into test
delete item 1 of line 3 of test
scummvmAssertEqual(test, "lorem" & RETURN & "ipsum" & RETURN & "sit,amet" & RETURN & "nunc")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor,sit,amet" & RETURN & "nunc" into test
delete item 2 of line 3 of test
scummvmAssertEqual(test, "lorem" & RETURN & "ipsum" & RETURN & "dolor,amet" & RETURN & "nunc")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor,sit,amet" & RETURN & "nunc" into test
delete item 3 of line 3 of test
scummvmAssertEqual(test, "lorem" & RETURN & "ipsum" & RETURN & "dolor,sit" & RETURN & "nunc")
put "lorem" & RETURN & "ipsum" & RETURN & "dolor,sit,amet" & RETURN & "nunc" into test
delete item 1 of line 2 of test
scummvmAssertEqual(test, "lorem" & RETURN & RETURN & "dolor,sit,amet" & RETURN & "nunc")
delete item 1 of line 2 of test
scummvmAssertEqual(test, "lorem" & RETURN & RETURN & "dolor,sit,amet" & RETURN & "nunc")
delete item 4 of line 2 of test
scummvmAssertEqual(test, "lorem" & RETURN & RETURN & "dolor,sit,amet" & RETURN & "nunc")
delete item 4 of line 4 of test
scummvmAssertEqual(test, "lorem" & RETURN & RETURN & "dolor,sit,amet" & RETURN & "nunc")
delete item 1 of line 4 of test
scummvmAssertEqual(test, "lorem" & RETURN & RETURN & "dolor,sit,amet" & RETURN)

View File

@@ -0,0 +1,249 @@
scummvmAssert(VOID = VOID)
scummvmAssert(not(VOID < VOID))
scummvmAssert(VOID <= VOID)
scummvmAssert(not(VOID > VOID))
scummvmAssert(VOID >= VOID)
scummvmAssert(VOID = 0)
scummvmAssert(VOID < 0)
scummvmAssert(VOID <= 0)
scummvmAssert(not(VOID > 0))
scummvmAssert(not(VOID >= 0))
scummvmAssert(not(0 = VOID))
scummvmAssert(0 > VOID)
scummvmAssert(0 >= VOID)
scummvmAssert(not(0 < VOID))
scummvmAssert(not(0 <= VOID))
scummvmAssert(1 = 1)
scummvmAssert(1 = "1")
scummvmAssert(1 = 1.0)
scummvmAssert(cast 1 = cast 1)
scummvmAssert("test" = "test")
scummvmAssert(#test = #test)
set string = the text of field 1
scummvmAssert(field 1 = string)
scummvmAssert(0 = "")
scummvmAssert(1 <> cast 1)
scummvmAssert("test" <> #test)
-- If string parses as a float, coerce to float
scummvmAssert("2000" > 25)
scummvmAssert(25 < "2000")
scummvmAssert("2000.5" > 25)
scummvmAssert(25 < "2000.5")
scummvmAssert("20e5" > 25)
scummvmAssert(25 < "20e5")
scummvmAssert(" 2000" > 25)
scummvmAssert(25 < " 2000")
scummvmAssert("2000" > 2.5)
scummvmAssert(25 < "2000")
scummvmAssert("2000.5" > 2.5)
scummvmAssert(2.5 < "2000.5")
scummvmAssert("20e5" > 2.5)
scummvmAssert(2.5 < "20e5")
scummvmAssert(" 2000" > 2.5)
scummvmAssert(2.5 < " 2000")
-- If a string doesn't parse as a float, coerce the number to a string
scummvmAssert("2/" < 20)
scummvmAssert(20 > "2/")
scummvmAssert("2000 e" < 25)
scummvmAssert(25 > "2000 e")
-- Two strings, treat with normal string ordering rules
scummvmAssert("2000" <> "2000.0")
scummvmAssert("2000" < "25")
scummvmAssert("abc" < "abcd")
scummvmAssert("abc" < "def")
-- Non-coercable string is always bigger than a number or void
scummvmAssert("test" > 3000)
scummvmAssert(3000 < "test")
scummvmAssert("test" > 300.0)
scummvmAssert(300.0 < "test")
scummvmAssert("test" > VOID)
scummvmAssert(VOID < "test")
-- Mimic an object
scummvmAssert("<Object:#FileIO" > 0)
-- Invalid comparisons should return FALSE
scummvmAssert(not (#test <= 0))
-- Picture comparisons are always false, even between the exact same cast.
-- set a to the picture of cast 1
-- scummvmAssert(a <> a)
-- scummvmAssert(a <> the picture of cast 1) -- always false
-- String comparison
scummvmAssert("a" = "A")
scummvmAssert(not("a" < "A"))
scummvmAssert(not("a" <= "A"))
scummvmAssert("a" > "A")
scummvmAssert("a" >= "A")
scummvmAssert(not("a" = "Z"))
scummvmAssert("a" < "Z")
scummvmAssert("a" <= "Z")
scummvmAssert(not("a" > "Z"))
scummvmAssert(not("a" >= "Z"))
scummvmAssert(not("Z" = "a"))
scummvmAssert(not("Z" < "a"))
scummvmAssert(not("Z" <= "a"))
scummvmAssert("Z" > "a")
scummvmAssert("Z" >= "a")
scummvmAssert(not("a" = "Bubba"))
scummvmAssert("a" < "Bubba")
scummvmAssert("a" <= "Bubba")
scummvmAssert(not("a" > "Bubba"))
scummvmAssert(not("a" >= "Bubba"))
scummvmAssert("z" = "Z")
scummvmAssert(not("z" < "Z"))
scummvmAssert(not("z" <= "Z"))
scummvmAssert("z" > "Z")
scummvmAssert("z" >= "Z")
scummvmAssert("abba" = "Abba")
scummvmAssert(not("abba" < "Abba"))
scummvmAssert(not("abba" <= "Abba"))
scummvmAssert("abba" > "Abba")
scummvmAssert("abba" >= "Abba")
scummvmAssert("Erwin's Catalyst" = "Erwin's Catalyst")
scummvmAssert(not("Erwin's Catalyst" < "Erwin's Catalyst"))
scummvmAssert("Erwin's Catalyst" <= "Erwin's Catalyst")
scummvmAssert(not("Erwin's Catalyst" > "Erwin's Catalyst"))
scummvmAssert("Erwin's Catalyst" >= "Erwin's Catalyst")
-- Char 213 resolves to U+2018 LEFT SINGLE QUOTATION MARK under D4-mac
scummvmAssert("Erwin's Catalyst" = ("Erwin" & numToChar(213) & "s Catalyst"))
scummvmAssert("Erwin's Catalyst" < ("Erwin" & numToChar(213) & "s Catalyst"))
scummvmAssert("Erwin's Catalyst" <= ("Erwin" & numToChar(213) & "s Catalyst"))
scummvmAssert(not("Erwin's Catalyst" > ("Erwin" & numToChar(213) & "s Catalyst")))
scummvmAssert(not("Erwin's Catalyst" >= ("Erwin" & numToChar(213) & "s Catalyst")))
-- Array comparison with coercion
scummvmAssert([] = [])
scummvmAssert(not([] <> []))
scummvmAssert([1, 2] = [1, 2])
scummvmAssert(not([1, 2] <> [1, 2]))
scummvmAssert([1, 2] = [1, "2"])
scummvmAssert(not([1, 2] <> [1, "2"]))
scummvmAssert([1, 2, 3] = [1, "2", 3.0])
scummvmAssert(not([1, 2, 3] <> [1, "2", 3.0]))
scummvmAssert(["testa", "testb"] = ["testa", "TESTB"])
scummvmAssert([#a: "testa", #b: "testb"] = [#a: "testa", #b: "TESTB"])
-- D4 has a quirk where only the left side list elements are checked
set the scummvmVersion to 400
scummvmAssert([] = [1, "2", 4])
scummvmAssert(not([] <> [1, "2", 4]))
scummvmAssert([1, 2] = [1, "2", 4])
scummvmAssert(not([1, 2] <> [1, "2", 4]))
scummvmAssert([1, 2, 3] <> [1, "2", 4])
scummvmAssert(not([1, 2, 3] = [1, "2", 4]))
scummvmAssert([1, 2, 3] <> [1, "2"])
scummvmAssert(not([1, 2, 3] = [1, "2"]))
scummvmAssert([:] = [#a: 1, #b: "2", #c: 4])
scummvmAssert(not([:] <> [#a: 1, #b: "2", #c: 4]))
scummvmAssert([#a: 1, #b: 2] = [#a: 1, #b: "2", #c: 4])
scummvmAssert(not([#a: 1, #b: 2] <> [#a: 1, #b: "2", #c: 4]))
scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2", #c: 4])
scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2", #c: 4]))
scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2"])
scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2"]))
set the scummvmVersion to 500
scummvmAssert([] <> [1, "2", 4])
scummvmAssert(not([] = [1, "2", 4]))
scummvmAssert([1, 2] <> [1, "2", 4])
scummvmAssert(not([1, 2] = [1, "2", 4]))
scummvmAssert([1, 2, 3] <> [1, "2", 4])
scummvmAssert(not([1, 2, 3] = [1, "2", 4]))
scummvmAssert([1, 2, 3] <> [1, "2"])
scummvmAssert(not([1, 2, 3] = [1, "2"]))
scummvmAssert([:] <> [#a: 1, #b: "2", #c: 4])
scummvmAssert(not([:] = [#a: 1, #b: "2", #c: 4]))
scummvmAssert([#a: 1, #b: 2] <> [#a: 1, #b: "2", #c: 4])
scummvmAssert(not([#a: 1, #b: 2] = [#a: 1, #b: "2", #c: 4]))
scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2", #c: 4])
scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2", #c: 4]))
scummvmAssert([#a: 1, #b: 2, #c: 3] <> [#a: 1, #b: "2"])
scummvmAssert(not([#a: 1, #b: 2, #c: 3] = [#a: 1, #b: "2"]))
-- single-element equality check, check to see if all list elements match
scummvmAssert(0 = [])
scummvmAssert([] = 0)
scummvmAssert(not(0 <> []))
scummvmAssert(not([] <> 0))
scummvmAssert(2 = [])
scummvmAssert([] = 2)
scummvmAssert(not(2 <> []))
scummvmAssert(not([] <> 2))
scummvmAssert(2 = [2, 2.0, "2", "2.0"])
scummvmAssert([2, 2.0, "2", "2.0"] = 2)
scummvmAssert(2 <> [2, 2.0, "2", "2.1"])
scummvmAssert([2, 2.0, "2", "2.1"] <> 2)
scummvmAssert(0 = [:])
scummvmAssert([:] = 0)
scummvmAssert(not(0 <> [:]))
scummvmAssert(not([:] <> 0))
scummvmAssert(2 = [:])
scummvmAssert([:] = 2)
scummvmAssert(not(2 <> [:]))
scummvmAssert(not([:] <> 2))
scummvmAssert(2 = [#a: 2, #b: 2.0, #c: "2", #d: "2.0"])
scummvmAssert([#a: 2, #b: 2.0, #c: "2", #d: "2.0"] = 2)
scummvmAssert(2 <> [#a: 2, #b: 2.0, #c: "2", #d: "2.1"])
scummvmAssert([#a: 2, #b: 2.0, #c: "2", #d: "2.1"] <> 2)
scummvmAssert(0 = point(0, 0))
scummvmAssert(point(0, 0) = 0)
scummvmAssert(2 = point(2, 2))
scummvmAssert(point(2, 2) = 2)
scummvmAssert(2 <> point(2, 3))
scummvmAssert(point(2, 3) <> 2)
scummvmAssert(0 = rect(0, 0, 0, 0))
scummvmAssert(rect(0, 0, 0, 0) = 0)
scummvmAssert(2 = rect(2, 2, 2, 2))
scummvmAssert(rect(2, 2, 2, 2) = 2)
scummvmAssert(2 <> rect(2, 2, 2, 3))
scummvmAssert(rect(2, 2, 2, 3) <> 2)
-- Void comparison
set v1 = value("!")
set v2 = value("!")
scummvmAssert(v1 = v2)
scummvmAssert(not(v1 < v2))
scummvmAssert(not(v1 > v2))
scummvmAssert(v1 = 0)
scummvmAssert(v1 < 0)
scummvmAssert(not(v1 > 0))
scummvmAssert(not(v1 = 0.0))
scummvmAssert(v1 < 0.0)
scummvmAssert(not(v1 > 0.0))
scummvmAssert(not(v1 = "0"))
scummvmAssert(v1 < "0")
scummvmAssert(not(v1 > "0"))
scummvmAssert(not(v1 = "what"))
scummvmAssert(v1 < "what")
scummvmAssert(not(v1 > "what"))

View File

@@ -0,0 +1,21 @@
on exitFrame
beep
end exitFrame
on enterFrame me
beep
end
on annoy howMuch
beep random(howMuch)
alert "Aren't handlers fun?"
end annoy
on annoy2 howMuch
beep random(howMuch)
alert "Aren't handlers fun?"
end annoy2 howMuch
on move me
set h = the locH of sprite pSprite + pChangeH
end

View File

@@ -0,0 +1,18 @@
abc(mNew)
factory abc
method mNew
instance avar
set avar to "a variable"
set res1 = me(callDo)
scummvmAssertEqual(res1, "a variable")
me(setDo)
scummvmAssertEqual(avar, 100)
method callDo
set myvar = 0
do("set myvar = avar")
return myvar
method setDo
do("set avar = 100")

View File

@@ -0,0 +1,143 @@
AimGun2
aim1(mDispose)
set notAMethod = 0
scummvmAssertError aim2(notAMethod) -- should error
aim2("notAMethod") -- should run fallback handler
-- method call syntax without parens
GrammarFactory mSetStatusToArg "foo"
scummvmAssertEqual status "foo"
GrammarFactory mSetStatusToArg2 "foo", "bar"
scummvmAssertEqual status "bar"
-- method call syntax with parens and a comma
GrammarFactory(mSetStatusToArg, "foo")
scummvmAssertEqual status "foo"
GrammarFactory(mSetStatusToArg2, "foo", "bar")
scummvmAssertEqual status "bar"
scummvmAssertEqual GrammarFactory(mReturnArg, "foo") "foo"
scummvmAssertEqual GrammarFactory(mReturnArg2, "foo", "bar") "bar"
-- method call syntax with parens and no comma
GrammarFactory(mSetStatusToArg "foo")
scummvmAssertEqual status "foo"
GrammarFactory(mSetStatusToArg2 "foo", "bar")
scummvmAssertEqual status "bar"
scummvmAssertEqual GrammarFactory(mReturnArg "foo") "foo"
scummvmAssertEqual GrammarFactory(mReturnArg2 "foo", "bar") "bar"
--
macro AimGun2
global aim1
set aim1 = aim2(mNew)
--
factory aim2
method mNew
dontpassevent
global aim1
when mousedown then aim1(fire)
when keydown then aim1(mExit)
set the locv of sprite 24 to 540
method mMove x, y
global aimCopy
set the locH of sprite 15 to x
set the locV of sprite 15 to y-250
set aimCopy = me
method mAtFrame
dontpassevent
me(mMove, the mouseH, the mouseV)
method fire
global fire1, targeth, targetv
set fire1 = fire2(mNew)
set the perframehook to fire1
me(mDispose)
method mExit
set the perframehook to false
postfire
me(mDispose)
method mDispose
global aim1
set aim1 = 1
when keydown then nothing
--
factory fire2
method mNew
dontpassevent
when mousedown then nothing
set the castnum of sprite 14 to f15
method mAtFrame
Global StartH, StartV, targetv, stepH, stepV, bcast
dontpassevent
set the castnum of sprite 14 to bcast
set the LocV of sprite 14 to (startV-stepV)
if sprite 14 intersects 10 and (startV-6) <= targetV then
set the castnum of sprite 14 to f16
set the perframehook to false
me(hit)
exit
end if
if startV < targetV or bcast>g17 then
set the perframehook to false
set the locV of sprite 14 to 340
aimgun2
exit
end if
set startV to (startV-stepV)
set bcast = bcast + 1
method hit
global KillLoc
set killloc to the loch of sprite 3
go "Death"
set the locV of sprite 14 to 400
aimgun2
if factory(killloc) then put "boo"
method mDispose
global fire1
set fire1 = 0
Method mNew2 theSprite, startCastRight, endCastRight, startCastLeft, endCastLeft
instance startDrawLeft, endDrawLeft, startDrawRight, endDrawRight, multiDrawP
if startCastLeft and endCastLeft then set multiDrawP to TRUE
set startDrawLeft to startCastRight
set endDrawLeft to endCastRight
set startDrawRight to startCastLeft
set endDrawRight to endCastLeft
me(mInitBirdPart, theSprite, startCastRight)
Method mAtFrame2 frame, subFrame
global gMouseH, gMouseV
set gMouseH = mouseH()
set gMouseV = mouseV()
-- Exit when "Done" button, which resides upon score channel 6.
if (the clickOn = 6) then exit
put the mouseDown into mouseClick
Body(mEveryFrame, mouseClick)
RightWing(mEveryFrame, mouseClick)
LeftWing(mEveryFrame, mouseClick)
--
macro KillIt2
global KillLoc
set the locH of sprite 3 to KillLoc
on aim2 test
put "fallback worked!"
end
--
factory GrammarFactory
method mSetStatusToArg arg
global status
set status = arg
method mSetStatusToArg2 arg1, arg2
global status
set status = arg2
method mReturnArg arg
return arg
method mReturnArg2 arg1, arg2
return arg2

View File

@@ -0,0 +1,27 @@
global version
on initPattern
global MaxPattern
set MaxPattern = 65
put "first: " & MaxPattern
set a = 5
end initPattern
on increase
global MaxPattern
set MaxPattern = MaxPattern + 1
put MaxPattern
set a = 10
end increase
set a = 8
set MaxPattern = 3
initPattern
increase
increase
put MaxPattern
put a

View File

@@ -0,0 +1,28 @@
go to frame "Open23" of movie "OpenCabin23"
go "CARDBACK"
go movie "BAR 1"
go to "Open23" of movie "OpenCabin23"
go to "Chair"
set varframe to "VARFrame"
set varmovie to "VARMovie"
go to frame 35
go the frame
go "label two"
go to movie "Chicago"
go to frame 23 of movie "Chicago"
go to frame "main loop" of movie "Basic With"
go to marker(1)
go frame "dissolve" of movie "captain's table"
play frame 37
play frame "label one"
play movie "Desert Scene"
play frame 23 of movie "Chicago"
play frame varframe of movie varmovie
play frame "main loop" of movie "Basic With"
play done
playAccel "Lungs.mma"
playAccel "Bolitas.mma", loop, clickStop, whatFits

View File

@@ -0,0 +1,13 @@
scummvmAssertEqual framesToHMS(123456, 30, FALSE, FALSE), " 01:08:35.06 "
scummvmAssertEqual framesToHMS(-123456, 30, FALSE, FALSE), "-01:08:35.06 "
scummvmAssertEqual framesToHMS(123456, 30, FALSE, TRUE), " 01:08:35.20 "
scummvmAssertEqual HMSToFrames(" 01:08:35.06 ", 30, FALSE, FALSE), 123456
scummvmAssertEqual HMSToFrames("-01:08:35.06 ", 30, FALSE, FALSE), -123456
scummvmAssertEqual HMSToFrames("01:08:35.06", 30, FALSE, FALSE), 123456
scummvmAssertEqual HMSToFrames("08:35.06", 30, FALSE, FALSE), 15456
scummvmAssertEqual HMSToFrames("35.06", 30, FALSE, FALSE), 1056
scummvmAssertEqual HMSToFrames("35", 30, FALSE, FALSE), 1050
scummvmAssertEqual HMSToFrames(" 01:08:35.20 ", 30, FALSE, TRUE), 123456

View File

@@ -0,0 +1,172 @@
--
set x = 1
if x = 5 then exit
else put 10.0
if x = 6 then put 2
set y = 4
-- defined variables are falsy when checked with not
-- i.e. not varundefined => true
-- only a var with value 0 is seen as false when checked with not
-- i.e. not 0 => true
scummvmAssert(varundefined = 0)
scummvmAssert(not varundefined = 1)
set a to 0
scummvmAssert(not a = 1)
set a to 0.0
scummvmAssert(not a = 0)
set a to "string"
scummvmAssert(not a = 0)
set parray to [#pt: 1, #ar: 0]
scummvmAssert(not parray = 0)
--
repeat with x = 1 to 6
if x = 3 then put 30
else if x = 4 then put 40
else if x = 5 then put 50
else put 10.0
if x = 1 then
put 1
else if x = 2 then
put 1232.12345678901234
put 2.2
else if x = 3 then
put 3
else if x = 4 then
put 4
else if x = 5 then
put 5
else if x = 6 then
put 6
end if
if x = 4 then put 40
else put 50
end repeat
if the keyCode = 36 then -- return key goes to first menu
go to frame 1
else if the keyCode = 123 then -- left arrow goes to previous menu
dontPassEvent
go to marker(-1)
else if the keyCode = 124 then -- right arrow goes to next menu
dontPassEvent
go to marker(1)
else if the keyCode = 125 then -- down arrow goes to last (bottom) menu
dontPassEvent
go to frame "credits"
else if the keyCode = 126 then -- up arrow goes to first (top) menu
dontPassEvent
go to frame 1
end if
if abs(y) = x and abs(x) = y then
put TRUE
else
put FALSE
end if
if abs(y) = x and abs(x) = y then put 5
if abs(y) = x and abs(x) = y then put 5
else put 6
if the selection = the text of cast 1 then
go to frame "sEnd"
exit
else if whichTry = 1 then go to frame "sTell"
else if whichTry = 2 then go to frame "sShow"
else if whichTry = 3 then go to frame "sDo"
if the selection = the text of cast 1 then
go to frame "sEnd"
exit
else if whichTry = 1 then
go to frame "sTell"
else if whichTry = 2 then
put 5
go to frame "sShow"
else if whichTry = 3 then go to frame "sDo"
else put 6
if the selection = the text of cast 1 then go to frame "sEnd"
else put 7
if the selection = the text of cast 1 then go to frame "sEnd"
else if whichTry = 1 then
go to frame "sTell"
else if whichTry = 2 then
put 5
go to frame "sShow"
else if whichTry = 3 then go to frame "sDo"
else
put 6
put 7
end if
if the selection = the text of cast 1 then go to frame "sEnd"
else if whichTry = 1 then
go to frame "sTell"
else if whichTry = 2 then
put 5
go to frame "sShow"
else if whichTry = 3 then go to frame "sDo"
else put 7
if rollOver(2) then
put "The cursor is INSIDE the square." into field "Cursor Status"
else
put "The cursor is OUTSIDE the square." into field "Cursor Status"
end if
if the selection = the text of cast 1 then
if whichTry = 3 then
go to frame "sDo"
end if
else if whichTry = 1 then
if whichTry = 3 then go to frame "sDo"
else
put 5
end if
else if whichTry = 2 then
if whichTry = 3 then go to frame "sDo"
else
if the selection = the text of cast A21 then
if whichTry = 3 then
go to frame "Foo"
end if
else if abra = 5 then put 6
else
put 7
if abra = 7 then put 5
end if
end if
else if whichTry = 3 then go to frame "sDo"
else put 6
if the selection = the text of cast 1 then go to frame "sEnd"
when keyDown then if the key = RETURN then checkField
when keyDown then if the key = QUOTE then checkField
if the mouseH > ((the right of sprite 15) - 20) then beep
else if ((the mouseH > ((the left of sprite 15) + 30)) and Â
(the mouseH < ((the left of sprite 15) + 73)) and Â
(the mouseV > ((the top of sprite 15) + 20)) and Â
(the mouseV < ((the top of sprite 15) + 40))) then StoreButtonHit
else if ((the mouseH > ((the left of sprite 15) + 81)) and Â
(the mouseH < ((the left of sprite 15) +124)) and Â
(the mouseV > ((the top of sprite 15) + 20)) and Â
(the mouseV < ((the top of sprite 15) + 40))) then LoadButtonHit
else if ((the mouseH > ((the left of sprite 15) + 30)) and Â
(the mouseH < ((the left of sprite 15) +124)) and Â
(the mouseV > ((the top of sprite 15) + 55)) and Â
(the mouseV < ((the top of sprite 15) + 83))) then AudioButtonHit
end if
macro WindowHorzP X
set X1 = constrainH( 14, X )
if X1 = X then return( TRUE )
else return( FALSE )

View File

@@ -0,0 +1,17 @@
-- single arg variant, return type
put ilk(10)
put ilk(20.0)
put ilk("Macromedia")
put ilk(point(10, 20))
put ilk(ilk(10))
set x = point(10, 20)
put ilk(x)
-- two arg variant, return if type matches
put ilk(10, #integer)
put ilk("Macromedia", #string)
set x = list(1,2,3)
put ilk(x, #list)
put ilk(x, #linearlist)
put ilk(x, #integer)

View File

View File

@@ -0,0 +1,34 @@
on count l
return "what is this"
end
on add l, x
return "this is the worst"
end
set result = count([1, 2, 3])
scummvmAssertEqual(result, 3)
set result = count([1: 2, 3: 4, 5: 6])
scummvmAssertEqual(result, 3)
set result = count(rect(1, 2, 3, 4))
scummvmAssertEqual(result, 4)
set result = count(point(1, 2))
scummvmAssertEqual(result, 2)
set result = count("not an array")
scummvmAssertEqual(result, "what is this")
set result = add("still not an array")
scummvmAssertEqual(result, "this is the worst")
set result = add("even less of an array", 8)
scummvmAssertEqual(result, "this is the worst")
set target = [1, 2, 3]
add(target, 4)
scummvmAssertEqual(count(target), 4)

View File

@@ -0,0 +1,371 @@
set x = []
set y = [:]
set machinery = [#gears:6, #balls:3, #ramps:8]
set nested = [1, 2, [3, 4], 5]
set gList = ["car":1, "boat": 20]
set a to [1, 2, 3]
put a
set gList = [point(70, 190), point(217, 66), point(364, 185)]
set gBugList = [[energy: 10, mood: "Happy"], [energy: -10, mood: "Sad"], [energy: 60, mood: "Hungry"], [energy: 20, mood: "Sad"]]
set b to [4, 5, 6, 7]
scummvmAssertEqual(string(a + b), "[5, 7, 9]")
scummvmAssertEqual(string(a - b), "[-3, -3, -3]")
scummvmAssertEqual(string(a * b), "[4, 10, 18]")
scummvmAssertEqual(string(b / a), "[4, 2, 2]")
scummvmAssertEqual(string(b mod a), "[0, 1, 0]")
scummvmAssertEqual(string(-a), "[-1, -2, -3]")
set floats to [4.0, 5.0, 6.0, 7.0]
set strings to ["4", "5", "6", "7"]
scummvmAssertEqual(a + floats, [5.0, 7.0, 9.0])
scummvmAssertEqual(a + strings, [5.0, 7.0, 9.0])
scummvmAssertEqual(string(a + 1), "[2, 3, 4]")
scummvmAssertEqual(string(1 + b), "[5, 6, 7, 8]")
-- property Array tests
set propArray to [501: "cast", 502: "value", 1.5: "script", a: "score", #b: "member", "color": "red"]
set var to getPropAt(propArray, 1)
scummvmAssertEqual(var, 501)
set var to getAt(propArray, 1)
scummvmAssertEqual(var, "cast")
set var to getProp(propArray, 1.5)
scummvmAssertEqual(var, "script")
set var to getProp(propArray, #a)
scummvmAssertEqual(var, "score")
set var to getProp(propArray, "a")
scummvmAssertEqual(var, "score")
set var to getProp(propArray, #color)
scummvmAssertEqual(var, "red")
set var to getProp(propArray, #b)
scummvmAssertEqual(var, "member")
-- itemOf
set string_array to "one,, three, four"
set res to item 2 of string_array
scummvmAssert(res="")
set res to item 3 of string_array
scummvmAssert(res=" three")
set res to item 4 of string_array
scummvmAssert(res=" four")
-- itemOf check for float
set res to item 3.4 of string_array
scummvmAssert(res=" three")
-- itemOf out of bounds checks
set res to item 5 of string_array
scummvmAssert(res="")
set res to item -1 of string_array
scummvmAssert(res=string_array)
-- itemOf: test delimiter
set save = the itemDelimiter
set the itemDelimiter = ":"
set delim_array to "one: two: three: four"
set res to item 3 of delim_array
scummvmAssert(res=" three")
set the itemDelimiter = save
-- rects
set rct to rect(0, 0, 100, 100)
set gA to getAt(rct, 2)
scummvmAssertEqual(gA, 0)
set gA to getAt(rct, 3)
scummvmAssertEqual(gA, 100)
setAt rct, 2, 20
scummvmAssertEqual(getAt(rct, 2), 20)
-- array conversions
set a to point(11, 12)
set b to rect(21, 22, 23, 24)
set c to [31]
set d to [41, 42]
set e to [51, 52, 53]
set f to [61, 62, 63, 64]
set g to [71, 72, 73, 74, 75]
set h to 5
scummvmAssertEqual(string(a + a), "point(22, 24)")
scummvmAssertEqual(string(a + b), "[32, 34]")
scummvmAssertEqual(string(a + c), "[42]")
scummvmAssertEqual(string(a + d), "point(52, 54)")
scummvmAssertEqual(string(a + e), "[62, 64]")
scummvmAssertEqual(string(a + f), "[72, 74]")
scummvmAssertEqual(string(a + g), "[82, 84]")
scummvmAssertEqual(string(a + h), "point(16, 17)")
scummvmAssertEqual(string(b + a), "[32, 34]")
scummvmAssertEqual(string(b + b), "rect(42, 44, 46, 48)")
scummvmAssertEqual(string(b + c), "[52]")
scummvmAssertEqual(string(b + d), "[62, 64]")
scummvmAssertEqual(string(b + e), "[72, 74, 76]")
scummvmAssertEqual(string(b + f), "rect(82, 84, 86, 88)")
scummvmAssertEqual(string(b + g), "[92, 94, 96, 98]")
scummvmAssertEqual(string(b + h), "rect(26, 27, 28, 29)")
scummvmAssertEqual(string(c + a), "[42]")
scummvmAssertEqual(string(c + b), "[52]")
scummvmAssertEqual(string(c + c), "[62]")
scummvmAssertEqual(string(c + d), "[72]")
scummvmAssertEqual(string(c + e), "[82]")
scummvmAssertEqual(string(c + f), "[92]")
scummvmAssertEqual(string(c + g), "[102]")
scummvmAssertEqual(string(c + h), "[36]")
scummvmAssertEqual(string(d + a), "[52, 54]")
scummvmAssertEqual(string(d + b), "[62, 64]")
scummvmAssertEqual(string(d + c), "[72]")
scummvmAssertEqual(string(d + d), "[82, 84]")
scummvmAssertEqual(string(d + e), "[92, 94]")
scummvmAssertEqual(string(d + f), "[102, 104]")
scummvmAssertEqual(string(d + g), "[112, 114]")
scummvmAssertEqual(string(d + h), "[46, 47]")
scummvmAssertEqual(string(e + a), "[62, 64]")
scummvmAssertEqual(string(e + b), "[72, 74, 76]")
scummvmAssertEqual(string(e + c), "[82]")
scummvmAssertEqual(string(e + d), "[92, 94]")
scummvmAssertEqual(string(e + e), "[102, 104, 106]")
scummvmAssertEqual(string(e + f), "[112, 114, 116]")
scummvmAssertEqual(string(e + g), "[122, 124, 126]")
scummvmAssertEqual(string(e + h), "[56, 57, 58]")
scummvmAssertEqual(string(f + a), "[72, 74]")
scummvmAssertEqual(string(f + b), "[82, 84, 86, 88]")
scummvmAssertEqual(string(f + c), "[92]")
scummvmAssertEqual(string(f + d), "[102, 104]")
scummvmAssertEqual(string(f + e), "[112, 114, 116]")
scummvmAssertEqual(string(f + f), "[122, 124, 126, 128]")
scummvmAssertEqual(string(f + g), "[132, 134, 136, 138]")
scummvmAssertEqual(string(f + h), "[66, 67, 68, 69]")
scummvmAssertEqual(string(g + a), "[82, 84]")
scummvmAssertEqual(string(g + b), "[92, 94, 96, 98]")
scummvmAssertEqual(string(g + c), "[102]")
scummvmAssertEqual(string(g + d), "[112, 114]")
scummvmAssertEqual(string(g + e), "[122, 124, 126]")
scummvmAssertEqual(string(g + f), "[132, 134, 136, 138]")
scummvmAssertEqual(string(g + g), "[142, 144, 146, 148, 150]")
scummvmAssertEqual(string(g + h), "[76, 77, 78, 79, 80]")
scummvmAssertEqual(string(h + a), "point(16, 17)")
scummvmAssertEqual(string(h + b), "rect(26, 27, 28, 29)")
scummvmAssertEqual(string(h + c), "[36]")
scummvmAssertEqual(string(h + d), "[46, 47]")
scummvmAssertEqual(string(h + e), "[56, 57, 58]")
scummvmAssertEqual(string(h + f), "[66, 67, 68, 69]")
scummvmAssertEqual(string(h + g), "[76, 77, 78, 79, 80]")
scummvmAssertEqual(string(h + h), "10")
-- proplist with missing keys
set proplist_without_keys = ["key": "value", "keyless expr 1", "keyless expr 2"]
set proplist_with_keys = ["key": "value", 2: "keyless expr 1", 3: "keyless expr 2"]
scummvmAssert(proplist_without_keys = proplist_with_keys)
-- list with symbol or string as key
set templst to [#mood: 1]
set tempmood to the mood of templst
scummvmAssert(tempmood = 1)
put templst
-- assign and check
set the mood of templst to 2
set tempmood to the mood of templst
scummvmAssert(tempmood = 2)
put templst
-- add
set list1 = [1, 4, 2]
add list1, 3
scummVMAssert(getPos(list1, 3) = 4)
set list1 = [1, 4, 2]
sort list1
add list1, 3
scummVMAssert(getPos(list1, 3) = 3)
-- addAt
-- addProp
-- append
-- count
-- deleteAt (uses getAt as basis)
set testList to [1, 2, 3, 4, 5]
deleteAt testList, 3
scummvmAssertEqual(testList, [1, 2, 4, 5])
set testList to [#a: 1, #b: 2, #c: 3, #d: 4, #e: 5]
deleteAt testList, 3
scummvmAssertEqual(testList, [#a: 1, #b: 2, #d: 4, #e: 5])
-- deleteOne (uses getOne as basis)
set testList to [5, 4, 3, 1, 4]
deleteOne testList, 4
scummvmAssertEqual(testList, [5, 3, 1, 4])
set testlist to [5, "4.0", 3, 1, 4]
deleteOne testList, 4
scummvmAssertEqual(testList, [5, 3, 1, 4])
set testlist to [5, 4.0, 3, 1, 4]
deleteOne testList, 4
scummvmAssertEqual(testList, [5, 3, 1, 4])
set testlist to [5, "urgh", 3, 1, "urgh"]
deleteOne testList, "urgh"
scummvmAssertEqual(testList, [5, 3, 1, "urgh"])
set testlist to [5, "URGH", 3, 1, "urgh"]
deleteOne testList, "urgh"
scummvmAssertEqual(testList, [5, "URGH", 3, 1])
-- deleteProp
-- findPos
set testList to [#a: 1, #b: 2, #bb: 3, #d: 4, #e: 5]
scummvmAssertEqual(findPos(testList, #b), 2)
scummvmAssertEqual(findPos(testList, #f), VOID)
set testList to []
scummvmAssertEqual(findPos(testList, #3), 0)
set testList to [1, 3, 0]
scummvmAssertEqual(findPos(testList, 3), 3)
scummvmAssertEqual(findPos(testList, 2), 2)
scummvmAssertEqual(findPos(testList, 4), 0)
scummvmAssertEqual(findPos(testList, -1), 0)
scummvmAssertEqual(findPos(testList, 1), 1)
sort testList
scummvmAssertEqual(findPos(testList, 1), 2)
scummvmAssertEqual(findPos(testList, 0), 1)
scummvmAssertEqual(findPos(testList, 2), 0)
scummvmAssertEqual(findPos(testList, 4), 0)
-- findPosNear
set testList to [#b: 2, #a: 1, #bb: 3, #d: 4, #e: 5]
scummvmAssertEqual(findPosNear(testList, #b), 1)
-- if the property list isn't sorted and the value isn't there, return the size of the list + 1
scummvmAssertEqual(findPosNear(testList, #missing), 6)
scummvmAssertEqual(findPosNear(testList, #bbb), 6)
sort testList
scummvmAssertEqual(findPosNear(testList, #bbb), 4)
set testList to [1: 2, 4: 8, 16: 32]
scummvmAssertEqual(findPosNear(testList, 4), 2)
scummvmAssertEqual(findPosNear(testList, 10), 4)
sort testList
scummvmAssertEqual(findPosNear(testList, 10), 3)
-- the manual claims that findPosNear throws an error if you give it a regular list. the manual is wrong.
set testList to [62, 5, 33]
scummvmAssertEqual(findPosNear(testList, 5), 2)
-- if the property list isn't sorted and the value isn't there, return the value
scummvmAssertEqual(findPosNear(testList, 44), 44)
sort testList
scummvmAssertEqual(findPosNear(testList, 5), 1)
scummvmAssertEqual(findPosNear(testList, 44), 3)
-- getaProp
set testList to [#a, #b, #c, #d, #e]
scummvmAssertEqual(getaProp(testList, 4), #d)
scummvmAssertError getaProp(testList, 7)
set testList to [#a: 5, #b: 4, #c: 3, #d: 2, #e: 1]
scummvmAssertEqual(getaProp(testList, #d), 2)
scummvmAssert(voidp(getaProp(testList, #g)))
-- getAt
set testList to [5, 4, 3, 2, 1]
scummvmAssertEqual(getAt(testList, 2), 4)
scummvmAssertError getAt(testList, 7)
set testList to [#a: 5, #b: 4, #c: 3, #d: 2, #e: 1]
scummvmAssertEqual(getAt(testList, 2), 4)
scummvmAssertError getAt(testList, 7)
-- getLast
set testList to [#a, #b, #c]
scummvmAssertEqual(getLast(testList), #c)
scummvmAssert(voidp(getLast([])))
set testList to [#a: 3, #b: 2, #c: 1]
scummvmAssertEqual(getLast(testList), 1)
scummvmAssert(voidp(getLast([:])))
-- getOne
set testList to [#a, #b, #c, #d, #d, #e]
scummvmAssertEqual(getOne(testList, #d), 4)
scummvmAssertEqual(getOne(testList, #g), 0)
set testList to [5, 4, 3, 1, 4]
scummvmAssertEqual(getOne(testList, 4), 2)
set testlist to [5, "4.0", 3, 1, 4]
scummvmAssertEqual(getOne(testList, 4), 2)
set testlist to [5, 4.0, 3, 1, 4]
scummvmAssertEqual(getOne(testList, 4), 2)
set testlist to [5, "urgh", 3, 1, "urgh"]
scummvmAssertEqual(getOne(testList, "urgh"), 2)
set testlist to [5, "URGH", 3, 1, "urgh"]
scummvmAssertEqual(getOne(testList, "urgh"), 5)
-- for finding ARRAY/PARRAY, check the pointer, not the contents
set testList to [#a, #b, [1, 2, 3]]
scummvmAssertEqual(getOne(testList, [1, 2, 3]), 0)
set subItem to [1, 2, 3]
set testList to [#a, #b, subItem]
scummvmAssertEqual(getOne(testList, subItem), 3)
-- getProp
set testList to [#a, #b, #c, #d, #e]
scummvmAssertEqual(getProp(testList, 3), #c)
-- getPropAt
-- ilk
-- list
-- listP
-- max
-- min
-- setaProp
-- setAt
set lst to []
setAt lst,1,5
scummvmAssertEqual(lst, [5])
set lst to []
setAt lst,3,5
scummvmAssertEqual(lst, [0,0,5])
setAt lst,2,5
scummvmAssertEqual(lst, [0,5,5])
-- setProp
-- sort

View File

@@ -0,0 +1,65 @@
set x = 5
if x <= 5 then set x = 6
if (x = 5) then
set x = 7 -- this is comment
else
set x = 8
-- this is another comment
end if
put x
-- this is more comment
set y = 1
repeat while (y < 5)
set y = y + 1
put y
end repeat
repeat with z = 10 to 15
put z
end repeat
repeat with y = 5 down to 1
put y
end repeat
repeat while y < 5
set y = y + 1
put y
end repeat
repeat while y < 5
set y = y + 1
if y = 3 then next repeat
put y
end repeat
-- tests for repeat with
on exitRepeatWith
set aList = [1,2,3,4]
repeat with a in aList
if a = 3 then
exit repeat
end if
end repeat
return a
end exitRepeatWith
on returnRepeatWith
set aList = [1,2,3,4]
repeat with a in aList
if a = 3 then
return a
end if
end repeat
end returnRepeatWith
on directListRepeatWith
repeat with a in [1,2,3,4]
put a
end repeat
end directListRepeatWith
put exitRepeatWith()
put returnRepeatWith()
directListRepeatWith()

View File

@@ -0,0 +1,44 @@
global x, y
set y = 8
shipx
put x
zipx
put x
put y
test
test()
put test
put test()
if test then test
--
macro SHIPX
global x, y
set x = Random(5)
if x = 1 then
go "Zoom"
exit
end if
if x >1 then
set y = 10
exit
end if
put 100
--
macro ZIPX
set x = Random(5)
if x = 1 then
go "ZIP"
exit
end if
if x >1 then
put x
check x, 5
exit
end if
--
macro test
return "a"

View File

@@ -0,0 +1,165 @@
if random(4) > 2 then put 1000
set z = 5.5
set z1 = 2
set z2 = z / z1
put z
put z1
put z2
put integer(z2)
put cos(z2)
set x = 2 + 3 * (4 / 2)
put x
put power(2, 8)
put power(2, 8, 0)
put power(2)
updatestage
-- Type conversion
put (1024/4096)*100 -- 0
put (1024/4096)*100.0 -- 0.0
put ((1024*1.0)/4096)*100.0 -- 25.0
put the sqrt of 9
scummvmAssertEqual(ilk(sqrt(EMPTY)), #float)
scummvmAssertEqual(sqrt(EMPTY), 0.0)
scummvmAssertEqual(ilk(sqrt(4)), #integer)
scummvmAssertEqual(string(sqrt(0)), "0")
scummvmAssertEqual(string(sqrt(4)), "2")
scummvmAssertEqual(string(sqrt(5)), "2")
scummvmAssertEqual(string(sqrt(8)), "3")
scummvmAssertEqual(ilk(sqrt(4.0)), #float)
scummvmAssertEqual(sqrt(0.0), 0.0)
scummvmAssertEqual(sqrt(4.0), 2.0)
scummvmAssertEqual(abs(sqrt(5.0) - 2.2361) < 0.0001, 1)
scummvmAssertEqual(abs(sqrt(8.0) - 2.8284) < 0.0001, 1)
scummvmAssertEqual(ilk(sqrt("4.0")), #float)
scummvmAssertEqual(sqrt("0.0"), 0.0)
scummvmAssertEqual(sqrt("4.0"), 2.0)
scummvmAssertEqual(abs(sqrt("5.0") - 2.2361) < 0.0001, 1)
scummvmAssertEqual(abs(sqrt("8.0") - 2.8284) < 0.0001, 1)
scummvmAssertEqual(ilk(sqrt("4")), #float)
scummvmAssertEqual(sqrt("0"), 0.0)
scummvmAssertEqual(sqrt("4"), 2.0)
scummvmAssertEqual(abs(sqrt("5") - 2.2361) < 0.0001, 1)
scummvmAssertEqual(abs(sqrt("8") - 2.8284) < 0.0001, 1)
-- Testing rounding
set save to the scummvmVersion
set the scummvmVersion to 300
scummvmAssertEqual(integer(2.5), 3)
scummvmAssertEqual(integer(2.49), 2)
scummvmAssertEqual(integer(2.1), 2)
scummvmAssertEqual(integer(2.0), 2)
scummvmAssertEqual(integer(1.9), 2)
scummvmAssertEqual(integer(1.5), 2)
scummvmAssertEqual(integer(1.49), 1)
scummvmAssertEqual(integer(1.0), 1)
scummvmAssertEqual(integer(0.0), 0)
scummvmAssertEqual(integer(-0.49), 0)
scummvmAssertEqual(integer(-0.5), 0)
scummvmAssertEqual(integer(-1.0), 0)
scummvmAssertEqual(integer(-1.49), 0)
scummvmAssertEqual(integer(-1.5), -1)
scummvmAssertEqual(integer(-1.9), -1)
scummvmAssertEqual(integer(-2.0), -1)
scummvmAssertEqual(integer(-2.1), -1)
scummvmAssertEqual(integer(-2.49), -1)
scummvmAssertEqual(integer(-2.5), -2)
set the scummvmVersion to save
-- Min/max - D4 has bugs related to handling VOID
set the scummvmVersion to 400
scummvmAssertEqual(min(VOID, 30), 30)
scummvmAssertEqual(ilk(max(VOID, 30)), #void)
scummvmAssertEqual(ilk(min(30, VOID)), #void)
scummvmAssertEqual(max(30, VOID), 30)
scummvmAssertEqual(min(VOID, "test"), "test")
scummvmAssertEqual(ilk(max(VOID, "test")), #void)
scummvmAssertEqual(ilk(min("test", VOID)), #void)
scummvmAssertEqual(max("test", VOID), "test")
scummvmAssertEqual(min(1, VOID, 3), 3)
scummvmAssertEqual(max(1, VOID, 3), 3)
scummvmAssertEqual(min(VOID, 3, "test"), 3)
scummvmAssertEqual(ilk(max(VOID, 3, "test")), #void)
scummvmAssertEqual(min(1, VOID, 3, "test"), 3)
scummvmAssertEqual(max(1, VOID, 3, "test"), "test")
scummvmAssertEqual(min(VOID, "test", 3), 3)
scummvmAssertEqual(ilk(max(VOID, "test", 3)), #void)
scummvmAssertEqual(min(3, VOID, "test"), "test")
scummvmAssertEqual(max(3, VOID, "test"), "test")
scummvmAssertEqual(ilk(min(3, "test", VOID)), #void)
scummvmAssertEqual(max(3, "test", VOID), "test")
scummvmAssertEqual(ilk(min("test", 3, VOID)), #void)
scummvmAssertEqual(max("test", 3, VOID), "test")
scummvmAssertEqual(min("test", VOID, 3), 3)
scummvmAssertEqual(max("test", VOID, 3), "test")
scummvmAssertEqual(min("test", VOID, 3, "2.5"), "2.5")
scummvmAssertEqual(max("test", VOID, 3, "2.5"), "test")
scummvmAssertEqual(min(1, "test", VOID, 3, "2.5"), "2.5")
scummvmAssertEqual(max(1, "test", VOID, 3, "2.5"), "test")
scummvmAssertEqual(min(1, "test", 3, "2.5"), 1)
scummvmAssertEqual(max(1, "test", 3, "2.5"), "test")
-- D5 fixes the VOID bug
set the scummvmVersion to 500
scummvmAssertEqual(ilk(min(VOID, 30)), #void)
scummvmAssertEqual(max(VOID, 30), 30)
scummvmAssertEqual(ilk(min(30, VOID)), #void)
scummvmAssertEqual(max(30, VOID), 30)
scummvmAssertEqual(min(VOID, "test"), "test")
scummvmAssertEqual(ilk(max(VOID, "test")), #void)
scummvmAssertEqual(min("test", VOID), "test")
scummvmAssertEqual(ilk(max("test", VOID)), #void)
scummvmAssertEqual(ilk(min(1, VOID, 3)), #void)
scummvmAssertEqual(max(1, VOID, 3), 3)
scummvmAssertEqual(min(VOID, 3, "test"), "test")
scummvmAssertEqual(max(VOID, 3, "test"), "test")
scummvmAssertEqual(min(1, VOID, 3, "test"), "test")
scummvmAssertEqual(max(1, VOID, 3, "test"), "test")
scummvmAssertEqual(min(VOID, "test", 3), 3)
scummvmAssertEqual(max(VOID, "test", 3), 3)
scummvmAssertEqual(min(3, VOID, "test"), "test")
scummvmAssertEqual(max(3, VOID, "test"), "test")
scummvmAssertEqual(ilk(min(3, "test", VOID)), #void)
scummvmAssertEqual(ilk(max(3, "test", VOID)), #void)
scummvmAssertEqual(ilk(min("test", 3, VOID)), #void)
scummvmAssertEqual(ilk(max("test", 3, VOID)), #void)
scummvmAssertEqual(min("test", VOID, 3), 3)
scummvmAssertEqual(max("test", VOID, 3), 3)
scummvmAssertEqual(min("test", VOID, 3, "2.5"), "2.5")
scummvmAssertEqual(max("test", VOID, 3, "2.5"), 3)
scummvmAssertEqual(min(1, "test", VOID, 3, "2.5"), "2.5")
scummvmAssertEqual(max(1, "test", VOID, 3, "2.5"), 3)
scummvmAssertEqual(min(1, "test", 3, "2.5"), 1)
scummvmAssertEqual(max(1, "test", 3, "2.5"), "test")
-- cases the same for both
set the scummvmVersion to 400
scummvmAssertEqual(min(29, "30.0"), 29)
scummvmAssertEqual(max(29, "30.0"), "30.0")
scummvmAssertEqual(min(30, "30.0"), 30)
scummvmAssertEqual(max(30, "30.0"), "30.0")
scummvmAssertEqual(min(31, "30.0"), "30.0")
scummvmAssertEqual(max(31, "30.0"), 31)
scummvmAssertEqual(min(30.0, "30.1"), 30.0)
scummvmAssertEqual(max(30.0, "30.1"), "30.1")
scummvmAssertEqual(min(30.2, "30.1"), "30.1")
scummvmAssertEqual(max(30.2, "30.1"), 30.2)
scummvmAssertEqual(min(50000, "test"), 50000)
scummvmAssertEqual(max(50000, "test"), "test")
scummvmAssertEqual(min(5000.0, "test"), 5000.0)
scummvmAssertEqual(max(5000.0, "test"), "test")

View File

@@ -0,0 +1,17 @@
mci "open MM\T005045a.wav type WaveAudio alias T005045a"
mci "play T005045a from 22710 to 32872"
sound playFile 1, "Jet Blast"
sound fadeOut 1
sound fadeOut 1, 20
sound fadeIn 1
sound fadeIn 1, 15
sound stop 1
playAccel "globe.mma", repeat, 5, noFlush, clickStop
playAccel "globe.mma", tempo, 100, repeat, 10, noFlush, clickStop
playAccel "globe.mma", tempo, 10, repeat, 4, noFlush, clickStop
mci
mci close all
mci play wave to 15228 hold

View File

@@ -0,0 +1,14 @@
put "ONE"
on two
put "TWO"
end two
put "THREE"
on four
put "FOUR"
end FOUR
four
two

View File

@@ -0,0 +1,25 @@
abc(mNew)
abc(callPerform)
factory abc
method mnew
put "init"
method callMe
put "Am i called?"
return "a1"
method callMe2
put "Am i called2?"
method callMe3
put "Am I called3?"
method callPerform
put "stepped into matFrame"
set retval to me(mPerform, "callMe")
me(mPerform, "callMe2")
me(mPerform, "callMe3")
put "returned" && retval
scummvmAssertEqual(retval, "a1")

View File

@@ -0,0 +1,3 @@
put point(10, 20)
set x = point(20,30)
put x

View File

@@ -0,0 +1,43 @@
put "qwertyuiop" into test
put "a" before char 3 of test
scummvmAssertEqual(test, "qwaertyuiop")
put "qwertyuiop" into test
put "a" into char 3 of test
scummvmAssertEqual(test, "qwartyuiop")
put "qwertyuiop" into test
put "a" after char 3 of test
scummvmAssertEqual(test, "qweartyuiop")
put "lorem ipsum dolor sit amet" into test
put "asdf" before word 3 of test
scummvmAssertEqual(test, "lorem ipsum asdfdolor sit amet")
put "lorem ipsum dolor sit amet" into test
put "asdf" into word 3 of test
scummvmAssertEqual(test, "lorem ipsum asdf sit amet")
put "lorem ipsum dolor sit amet" into test
put "asdf" after word 3 of test
scummvmAssertEqual(test, "lorem ipsum dolorasdf sit amet")
put "123456789" into test
put "foo" into char 20 of test
scummvmAssertEqual(test, "123456789 foo")
put "foo" into test
put "bar" into word 10000 of test
scummvmAssertEqual(test, "foobar")
put "foo" into test
put "bar" into item 10 of test
scummvmAssertEqual(test, "foo,,,,,,,,,bar")
put "foo" into test"
put "bar" into line 10 of test
scummvmAssertEqual(test, "foo" & RETURN & RETURN & RETURN & RETURN & RETURN & RETURN & RETURN & RETURN & RETURN & "bar")
put "foo" & RETURN & "lorem ipsum" into test
put "bar" into char 3 of word 2 of line 2 of test
scummvmAssertEqual(test, "foo" & RETURN & "lorem ipbarum")

View File

@@ -0,0 +1,50 @@
-- basic references
set a = cast 1
set a = cast "castname"
set a = field 1
set a = field "castname"
set a = script "scriptname"
set a = window "windowname"
-- assign to field reference
put "asdf" into field 1
put "lorem ipsum" into cast 1
-- the property of reference
put the text of cast 1
put line 1 to 5 of field the number of cast "MasterList" into field the number of cast "InventoryList"
-- the loaded of cast
-- real casts
scummvmAssert(the loaded of cast 1)
set test to cast 1
scummvmAssert(the loaded of test)
-- nonexistent casts
scummvmAssert(not the loaded of cast 500)
set test to cast 500
scummvmAssert(not the loaded of test)
put the loaded of cast "fake"
set test to cast "fake"
-- the following test should not be executed
-- we are testing only compilation
if scummvmCompileOnly = 1337 then
-- user-defined handlers/factories w/ reference name
-- (mainly to test grammar, so no factory definition)
set theWindow = Window(mNew,#noGrowDoc,"Window " & windowNumber)
-- other weird stuff with reference name
-- FIXME: We need to find a way of clean execution of these
put window("test")
set window = 1
put window
window("test")
window "test"
window cast
fi
put cast cast

View File

@@ -0,0 +1,6 @@
set z = "foo bar baz"
set z1 = z & " meow"
set z1 = z1 && "woof"
scummvmAssert(z1 contains "MeÍW")
scummvmAssert("meow" = "MeÍW")

View File

@@ -0,0 +1,24 @@
-- Just ints, use numeric sort
set test to [200, 100, 25, 4, 3, 2, 1]
sort(test)
scummvmAssertEqual(test, [1, 2, 3, 4, 25, 100, 200])
-- Just floats, use numeric sort
set test to [200.5, 100.5, 25.5, 4.5, 3.5, 2.5, 1.5]
sort(test)
scummvmAssertEqual(test, [1.5, 2.5, 3.5, 4.5, 25.5, 100.5, 200.5])
-- ints and floats, use numeric sort
set test to [200, 100, 25, 4, 3, 2, 1, 2.5]
sort(test)
scummvmAssertEqual(test, [1, 2, 2.5, 3, 4, 25, 100, 200])
-- Just strings, use string sort
set test to ["200", "100", "25", "4", "3", "2", "1", "2.5", "oh no"]
sort(test)
scummvmAssertEqual(test, ["1", "100", "2", "2.5", "200", "25", "3", "4", "oh no"])
-- For combined types (e.g. ints and strings), the result is undefined.
-- The real interpreter will give an answer that's nearly the same as the
-- string sort order, or softlock.

View File

@@ -0,0 +1,6 @@
Sound playfile 3, "Sound.AIF"
sound fadeIn 5
sound fadeIn 5, 10
sound stop 1
sound close 1

View File

@@ -0,0 +1,191 @@
set z = "foo bar baz"
set z1 = z & " meow"
set z1 = z1 && "woof"
scummvmAssert(z1 contains "bar")
set c = chars("Macromedia", 6, 6)
scummvmAssertEqual(c, "m")
set c = chars("Macromedia", 6, 10)
scummvmAssertEqual(c, "media")
set c = chars("Macromedia", -1, 15)
scummvmAssertEqual(c, "Macromedia")
set save to the scummvmVersion
set the scummvmVersion to 300
set c = chars("Macromedia", 1, 1.1)
scummvmAssertEqual(c, 0)
set the scummvmVersion to save
put "That is the last line of the file." & return & "Click Done to exit." && return && "foo"
if the key = Return then
dontPassEvent
end if
when keydown then if the key=return then set the stagecolor to random (256)
put return
put "foo" into test
put return after test
put test
scummvmAssertEqual(test, "foo" & return)
put return before test
scummvmAssertEqual(test, return & "foo" & return)
put return into test
scummvmAssertEqual(test, return)
-- coercing strings to numbers
-- str(int) + int
scummvmAssertEqual("2" + 5, 7.0)
scummvmAssertEqual(5 + "2", 7.0)
-- str(int) + float
scummvmAssertEqual("2" + 5.5, 7.5)
scummvmAssertEqual(5.5 + "2", 7.5)
-- str(float) + int
scummvmAssertEqual("2.5" + 5, 7.5)
scummvmAssertEqual(5 + "2.5", 7.5)
-- str(float) + float
scummvmAssertEqual("2.25" + 5.5, 7.75)
scummvmAssertEqual(5.5 + "2.25", 7.75)
-- str(int) + str(int)
scummvmAssertEqual("2" + "5", 7.0)
scummvmAssertEqual("5" + "2", 7.0)
-- str(int) + str(float)
scummvmAssertEqual("2" + "5.5", 7.5)
scummvmAssertEqual("5.5" + "2", 7.5)
-- str(float) + str(int)
scummvmAssertEqual("2.5" + "5", 7.5)
scummvmAssertEqual("5" + "2.5", 7.5)
-- str(float) + str(float)
scummvmAssertEqual("2.25" + "5.5", 7.75)
scummvmAssertEqual("5.5" + "2.25", 7.75)
-- float syntax
scummvmAssertEqual("-2" + 5, 3.0)
scummvmAssertEqual("-2.5" + 5, 2.5)
scummvmAssertEqual("+2" + 5, 7.0)
scummvmAssertEqual("+2.5" + 5, 7.5)
scummvmAssertEqual("2e3" + 5, 2005.0)
scummvmAssertEqual("2.5e3" + 5, 2505.0)
scummvmAssertEqual(" 2" + 5, 7.0)
scummvmAssertEqual(" 2.5" + 5, 7.5)
-- non number strings should coerce to a pointer
scummvmAssertEqual(ilk("incorrect" + 5), #integer)
scummvmAssertEqual(ilk(" 2.5 " + 5), #integer)
scummvmAssert(ilk("2 uhhh" + 5), #integer)
scummvmAssert(ilk("2.5 uhhh" + 5), #integer)
put "sausages" into testString
put (testString + 0) into testPointer
scummvmAssertEqual(ilk(testPointer), #integer)
scummvmAssertEqual(testString + 4, testPointer + 4)
scummvmAssertEqual(testString - 4, testPointer - 4)
scummvmAssertEqual(testString * 4, testPointer * 4)
scummvmAssertEqual(testString / 4, testPointer / 4)
-- same horrible logic should apply to symbols
put #haggis into testString
put (testString + 0) into testPointer
scummvmAssertEqual(ilk(testPointer), #integer)
scummvmAssertEqual(testString + 4, testPointer + 4)
scummvmAssertEqual(testString - 4, testPointer - 4)
scummvmAssertEqual(testString * 4, testPointer * 4)
scummvmAssertEqual(testString / 4, testPointer / 4)
-- casting to integer
scummvmAssertEqual(integer("2"), 2)
scummvmAssertEqual(integer("-2"), -2)
scummvmAssertEqual(integer(" 2"), 2)
scummvmAssertEqual(integer("2.5"), VOID)
scummvmAssertEqual(integer(" 2.5"), VOID)
scummvmAssertEqual(integer("incorrect"), VOID)
scummvmAssertEqual(integer("2 extra"), 2)
scummvmAssertEqual(integer("2.5 extra"), VOID)
scummvmAssertEqual(integer(" 2 extra"), 2)
scummvmAssertEqual(integer(" 2.5 extra"), VOID)
scummvmAssertEqual(integer("2extra"), VOID)
scummvmAssertEqual(integer(" 2extra"), VOID)
-- casting to float
scummvmAssertEqual(float("2"), 2.0)
scummvmAssertEqual(float("-2"), -2.0)
scummvmAssertEqual(float(" 2"), 2.0)
scummvmAssertEqual(float("2.5"), 2.5)
scummvmAssertEqual(float(" 2.5"), 2.5)
scummvmAssertEqual(float("incorrect"), "incorrect")
scummvmAssertEqual(float("2 extra"), "2 extra")
scummvmAssertEqual(float("2.5 extra"), "2.5 extra")
scummvmAssertEqual(float(" 2 extra"), " 2 extra")
scummvmAssertEqual(float(" 2.5 extra"), " 2.5 extra")
scummvmAssertEqual(float("2extra"), "2extra")
scummvmAssertEqual(float(" 2extra"), " 2extra")
-- LC::charOF
set string to "Macromedia"
set res to char 6 of string
scummvmAssertEqual(res, "m")
-- error and bounds checks
set res to char 60 of string
scummvmAssertEqual(res, EMPTY)
set res to char 0 of string
scummvmAssertEqual(res, string)
-- Test D4, it does a floor
set res to char 5.49 of string
scummvmAssertEqual(res, "o")
set res to char 5.5 of string
scummvmAssertEqual(res, "o")
-- Test D3, it does a round
set save to the scummvmVersion
set the scummvmVersion to 300
set res to char 5.49 of string
scummvmAssertEqual(res, "o")
set res to char 5.5 of string
scummvmAssertEqual(res, "m")
set the scummvmVersion to save
-- LC::charToOf
set string to "Macromedia"
set res to char 6 to 9 of string
scummvmAssertEqual(res, "medi")
-- error and bounds checks
set res to char 5.4 to 7.9 of string
scummvmAssertEqual(res, "ome")
set res to char 6 to 5 of string
scummvmAssertEqual(res, "")
set res to char 6 to 60 of string
scummvmAssertEqual(res, "media")
set res to char -1 to -2 of string
scummvmAssertEqual(res, string)
set res to char 50 to 60 of string
scummvmAssertEqual(res, "")
-- LB::b_lastcharof
scummvmAssertEqual(the last char of "", "")
scummvmAssertEqual(the last char of "hello", "o")
-- LB::b_lastitemof
scummvmAssertEqual(the last item of "", "")
scummvmAssertEqual(the last item of "onetwo", "onetwo")
scummvmAssertEqual(the last item of "one,two", "two")
set save to the itemDelimiter
set the itemDelimiter to ":"
scummvmAssertEqual(the last item of "one:two", "two")
set the itemDelimiter to save
scummvmAssertEqual(the last item of "onetwo", "onetwo")
-- LB::b_offset
set pos to offset("mov", "mov")
scummvmAssertEqual(pos, 1)
set pos to offset("mov", "C:\GAMES\OPEN.MOV")
scummvmAssertEqual(pos, 15)

View File

@@ -0,0 +1,9 @@
tell window "ball" to puppettempo 5
tell window "hello" to continue
tell window childMovie
go to frame 50
set the stageColor to 100
updateStage
end tell

View File

@@ -0,0 +1,95 @@
put 1.0 / 3
set the floatPrecision to 6
put 1.0 / 3
put the loch of sprite 4
set the loch of sprite 5 to 10
set the castnum of sprite 8 to the number of cast "A Blank Castmember"
put the time
put the abbrev time
put the long time
put the date
put the short date
put the abbrev date
put the long date
set a = 5
set gravityConst = the value of ( QUOTE & the text of cast 1 & QUOTE )
set the hilite of cast 1 to (a = 3)
set the text of cast 1 = string( gravityConst )
-- This is object setting. D4+
set BlastWindow to window "Media Blast"
set the filename of BlastWindow to "blastwin"
set the titleVisible of BlastWindow to true
set the modal of BlastWindow to true
set the windowtype of BlastWindow to 5
set the bottom of sprite 1 to originV + (the number of lines in someText) * 16
set the bottom of sprite 1 to originV + (the number of words in someText) * 16
put the number of words of field 1 into field 2
set pSprite to 1
set pChangeH to 10
set h = the locH of sprite pSprite + pChangeH
set the volume of sound 1 to 255
put the volume of sound 1 into field 1
set the checkMark of menuItem "Horizontal" of menu "The Ball" to False
put the number of castMembers into field 2
put the number of menuItems of menu "sam" into field 2
put abs( the locH of sprite 2 - the mouseH) into field 2
set the castNum of sprite the clickon = the number of cast 1
-- set castname = the name of cast(the castnum of sprite 1)
set res = the soundBusy of 1
put the sqrt of 9
put 5 into field the number of cast "MasterList"
set the text of field 1 = "Hello"
-- The next statement is valid lingo, however it's hard to parse.
-- We have no indication that this one off type of code is used.
-- set the text of field 0 + 1 = "Hello"
set h to "Hello"
set gMarkerName = the length of h + 2
scummvmAssertEqual(0, the length of 123)
-- test the itemDelimiter
set save = the itemDelimiter
scummvmAssert(save=",")
set the itemDelimiter = ":"
scummvmAssert(the itemDelimiter=":")
set the itemDelimiter = ":,"
scummvmAssert(the itemDelimiter=":")
set the itemDelimiter = ""
scummvmAssert(the itemDelimiter="")
set the itemDelimiter = save
-- test number of items
set chunkExpr to "one:two:three"
scummvmAssertEqual(the number of items in chunkExpr, 1)
set the itemDelimiter to ":"
scummvmAssertEqual(the number of items in chunkExpr, 3)
set the itemDelimiter to save
-- test initialisation
scummvmAssertEqual(the beepOn, 0)
scummvmAssertEqual(the keyDownScript, "")
scummvmAssertEqual(the mouseDownScript, "")
scummvmAssertEqual(the mouseUpScript, "")

View File

@@ -0,0 +1,10 @@
on test a, b, c,
put a
end
global foo, bar,
property baz,
test "foo", "bar", "baz",
put sqrt(4,)

View File

@@ -0,0 +1,25 @@
-- int
scummvmAssertEqual(55, value("55"))
-- float
scummvmAssertEqual(5.55, value("5.55"))
-- array
scummvmAssertEqual([1, 2, 3], value("[1, 2, 3]"))
-- parray
scummvmAssertEqual([#a: 1, #b: 2], value("[#a: 1, #b: 2]"))
-- expressions
scummvmAssertEqual(9, value("3*3"))
-- the kicker; you are allowed to have garbage on the end!!!
-- if it hits a token it doesn't understand, the parser should try again but stopping just before that token.
scummvmNoFatalError(true)
scummvmAssertEqual(9, value("3*3[34]"))
scummvmAssertEqual([1, 2, 3], value("[1, 2, 3],4]"))
scummvmNoFatalError(false)
-- if there's no valid expression at all, return void
set test = value("#")
scummvmAssert(voidP(test))

View File

@@ -0,0 +1,7 @@
set a.1 = 2
set a1 = 3
set a1. = 4
scummvmAssert(a.1=2)
scummvmAssert(a1=3)
scummvmAssert(a1.=4)

View File

@@ -0,0 +1,120 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*************************************
*
* USED IN:
* Gahan Wilson's Ultimate Haunted House
*
*************************************/
/*
* -- copyright 1994 by Byron Priess Multimedia, authored by MayoSmith and Lee
* aiff
* I mNew --Read Docs to avoid Hard Drive failure
* X mDispose --Disposes of XObject instance
* S mName --Returns the XObject name (Widget)
* I mStatus --Returns an integer status code
* SI mError, code --Returns an error string
* S mLastError --Returns last error string
* III mAdd, arg1, arg2 --Returns arg1+arg2
* SSI mFirst, str, nchars --Return the first nchars of string str
* V mMul, f1, f2 --Returns f1*f2 as floating point
* X mGlobals --Sample code to Read & Modify globals
* X mSymbols --Sample code to work with Symbols
* X mSendPerform --Sample code to show SendPerform call
* X mFactory --Sample code to find Factory objects
* IS mDuration, str --Read Docs to avoid Hard Drive failure
*/
#include "common/macresman.h"
#include "audio/decoders/aiff.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/xlibs/a/aiff.h"
namespace Director {
const char *const AiffXObj::xlibName = "aiff";
const XlibFileDesc AiffXObj::fileNames[] = {
{ "AIFF", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{ "new", AiffXObj::m_new, 0, 0, 400 }, // D4
{ "Duration", AiffXObj::m_duration, 1, 1, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
void AiffXObj::open(ObjectType type, const Common::Path &path) {
if (type == kXObj) {
AiffXObject::initMethods(xlibMethods);
AiffXObject *xobj = new AiffXObject(kXObj);
g_lingo->exposeXObject(xlibName, xobj);
}
}
void AiffXObj::close(ObjectType type) {
if (type == kXObj) {
AiffXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
}
AiffXObject::AiffXObject(ObjectType ObjectType) :Object<AiffXObject>("Aiff") {
_objType = ObjectType;
}
void AiffXObj::m_new(int nargs) {
g_lingo->printSTUBWithArglist("AiffXObj::new", nargs);
g_lingo->push(g_lingo->_state->me);
}
void AiffXObj::m_duration(int nargs) {
Common::String filePath = g_lingo->pop().asString();
Common::SeekableReadStream *aiffStream = Common::MacResManager::openFileOrDataFork(findPath(filePath));
if (!aiffStream) {
warning("AiffXObj::m_duration: Failed to open %s", filePath.c_str());
g_lingo->push(0);
return;
}
Audio::AIFFHeader *aiffHeader = Audio::AIFFHeader::readAIFFHeader(aiffStream, DisposeAfterUse::NO);
if (!aiffHeader) {
warning("AiffXObj::m_duration: No AIFF header found for %s", filePath.c_str());
g_lingo->push(0);
delete aiffStream;
return;
}
int duration = (aiffHeader->getFrameCount() / (float)aiffHeader->getFrameRate()) * 60;
delete aiffHeader;
delete aiffStream;
g_lingo->push(Datum(duration));
}
} // End of namespace Director

View File

@@ -0,0 +1,49 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_XLIBS_AIFF_H
#define DIRECTOR_LINGO_XLIBS_AIFF_H
namespace Director {
class AiffXObject : public Object<AiffXObject> {
public:
AiffXObject(ObjectType objType);
};
namespace AiffXObj {
extern const char *const xlibName;
extern const XlibFileDesc fileNames[];
void open(ObjectType type, const Common::Path &path);
void close(ObjectType type);
void m_new(int nargs);
void m_duration(int nargs);
} // End of namespace AiffXObj
} // End of namespace Director
#endif

View File

@@ -0,0 +1,321 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*************************************
*
* USED IN:
* Classical Cats
* The Daedalus Encounter
* Refixion II
*
*************************************/
/*
-! Category compactDisc
* -! Title AppleCD SC
* -! Protocol Ortho-Play 1.6.1
* --© 1990,1991 MacroMind, Inc. Alan McNeil
* ------------------------------------------------------
* --AppleCD SC, version 2.5.6
* --Mar 7 1994
* --including AppleCD SC Plus volume control
* ------------------------------------------------------
* X mNew
* X mDispose
* --
* I mGetMaxNodes
* SI mGetNodeTitle NodeNum
* II mSelectNode NodeNum
* --
* I mService
* I mGetValue
* I mCancel
* S mExplain
* I mIdle
* --
* X mReadStatus
* X mReadPos
* XI mSearchTo position
* X mPlay
* X mStill
* X mStop
* X mScanForward
* X mScanReverse
* X mEject
* --
* I mGetFirstTrack
* I mGetLastTrack
* II mGetFirstFrame tracknum
* II mGetLastFrame tracknum
* I mGetTrack
* XII mAudioEnable number number
* I mGetFrameResolution
* --
* II mSetInPoint frame
* II mSetOutPoint frame
* II mSetDuration frames
* --
* X mPlayCue
* X mPlaySegment
* --
* S mTitle
* SI mTrackName tracknum
* IS mSetTitle string
* IIS mSetTrackName tracknum string
* II mGetTrackType tracknum
* III mSetVolume leftVolume rightVolume
* II mGetVolume leftFlag
* --
*/
#include "backends/audiocd/audiocd.h"
#include "common/file.h"
#include "common/formats/cue.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/xlibs/a/applecdxobj.h"
namespace Director {
const char *const AppleCDXObj::xlibName = "AppleCD";
const XlibFileDesc AppleCDXObj::fileNames[] = {
{ "AppleCD", nullptr },
{ "AppleCD XObj", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{ "new", AppleCDXObj::m_new, 0, 0, 300 }, // D3
{ "dispose", AppleCDXObj::m_dispose, 0, 0, 300 }, // D3
{ "Service", AppleCDXObj::m_service, 0, 0, 300 }, // D4
{ "Still", AppleCDXObj::m_still, 0, 0, 300 }, // D3
{ "ReadStatus", AppleCDXObj::m_readStatus, 0, 0, 300 }, // D3
{ "GetValue", AppleCDXObj::m_getValue, 0, 0, 300 }, // D3
{ "Eject", AppleCDXObj::m_eject, 0, 0, 300 }, // D3
{ "GetFirstTrack", AppleCDXObj::m_getFirstTrack, 0, 0, 300 }, // D3
{ "GetLastTrack", AppleCDXObj::m_getLastTrack, 0, 0, 300 }, // D3
{ "GetFirstFrame", AppleCDXObj::m_getFirstFrame, 1, 1, 300 }, // D3
{ "GetLastFrame", AppleCDXObj::m_getLastFrame, 1, 1, 300 }, // D3
{ "SetInPoint", AppleCDXObj::m_setInPoint, 1, 1, 300 }, // D3
{ "SetOutPoint", AppleCDXObj::m_setOutPoint, 1, 1, 300 }, // D3
{ "PlayCue", AppleCDXObj::m_playCue, 0, 0, 300 }, // D3
{ "PlaySegment", AppleCDXObj::m_playSegment, 0, 0, 300 }, // D3
{ "ReadPos", AppleCDXObj::m_readPos, 0, 0, 300 }, // D3
{ nullptr, nullptr, 0, 0, 0 }
};
void AppleCDXObj::open(ObjectType type, const Common::Path &path) {
if (type == kXObj) {
AppleCDXObject::initMethods(xlibMethods);
AppleCDXObject *xobj = new AppleCDXObject(kXObj);
g_lingo->exposeXObject(xlibName, xobj);
}
}
void AppleCDXObj::close(ObjectType type) {
if (type == kXObj) {
AppleCDXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
}
AppleCDXObject::AppleCDXObject(ObjectType ObjectType) :Object<AppleCDXObject>("AppleCD") {
_objType = ObjectType;
_inpoint = 0;
_outpoint = 0;
Common::File cuefile;
if (cuefile.open("disc.cue")) {
Common::String cuestring = cuefile.readString(0, cuefile.size());
_cue = Common::SharedPtr<Common::CueSheet>(new Common::CueSheet(cuestring.c_str()));
}
}
void AppleCDXObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
void AppleCDXObj::m_dispose(int nargs) {
g_director->_system->getAudioCDManager()->stop();
}
void AppleCDXObj::m_still(int nargs) {
g_director->_system->getAudioCDManager()->stop();
}
// Not implemented yet; needs to be able to return appropriate values
// for playing/paused.
void AppleCDXObj::m_service(int nargs) {
g_lingo->push(Datum(0));
}
void AppleCDXObj::m_readStatus(int nargs) {
}
void AppleCDXObj::m_getValue(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
g_lingo->push(Datum(me->_returnValue));
}
void AppleCDXObj::m_setInPoint(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
int inpoint = g_lingo->pop().asInt();
debug(5, "AppleCDXObj::setInPoint: %i", inpoint);
me->_inpoint = inpoint;
}
void AppleCDXObj::m_setOutPoint(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
int outpoint = g_lingo->pop().asInt();
debug(5, "AppleCDXObj::setOutPoint: %i", outpoint);
me->_outpoint = outpoint;
}
void AppleCDXObj::m_playCue(int nargs) {
// Essentially a noop for us; this asks the drive to seek to that point,
// then poll until it does. We don't have seek times, so we'll
// simply noop.
}
void AppleCDXObj::m_playSegment(int nargs) {
// Performs playback at the pre-specified absolute point on the disc,
// using the values from setInPoint and setOutPoint
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
g_director->_system->getAudioCDManager()->playAbsolute(me->_inpoint, -1, 0, 0);
}
void AppleCDXObj::m_readPos(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
AudioCDManager::Status status = g_director->_system->getAudioCDManager()->getStatus();
if (me->_cue) {
// ScummVM currently doesn't support specifying the exact frame, so we pretend we're at the first frame of the song
Common::CueSheet::CueTrack *track = me->_cue->getTrack(status.track);
if (track != nullptr) {
me->_returnValue = track->indices[0];
}
}
}
void AppleCDXObj::m_getFirstTrack(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
if (me->_cue) {
Common::Array<Common::CueSheet::CueTrack> tracks = me->_cue->tracks();
// If we have a set of tracks parsed, return the first track's number
int index;
if (tracks.size() >= 1) {
index = tracks[0].number;
} else {
index = 1;
}
debug(5, "AppleCDXObj::m_getFirstTrack: returning %i", index);
g_lingo->push(Datum(index));
} else {
// If we don't have a TOC, just assume the first track is 1
debug(5, "AppleCDXObj::m_getFirstTrack: returning default");
g_lingo->push(Datum(1));
}
}
void AppleCDXObj::m_getLastTrack(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
if (me->_cue) {
Common::Array<Common::CueSheet::CueTrack> tracks = me->_cue->tracks();
// If we have a set of tracks parsed, return the final track's number
int index;
if (tracks.size() >= 1) {
index = tracks.back().number;
} else {
index = 1;
}
debug(5, "AppleCDXObj::m_getLastTrack: returning %i", index);
} else {
// If we don't have a TOC, just assume the last track is 1
debug(5, "AppleCDXObj::m_getLastTrack: returning default");
g_lingo->push(Datum(1));
}
}
void AppleCDXObj::m_getFirstFrame(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
int trackNum = g_lingo->pop().asInt();
if (me->_cue) {
Common::CueSheet::CueTrack *track = me->_cue->getTrack(trackNum);
// We use index 1 here because index 0 is typically the
// pregap, and we don't want to describe the first sector of the
// pregap as being the first sector of the track. Most discs
// will have only two indices, and the second index is where the
// actual track begins.
int index = track->indices.size() > 1 ? track->indices[1] : track->indices[0];
debug(5, "AppleCDXObj::m_getFirstFrame(%i): returning %i", trackNum, index);
g_lingo->push(Datum(index));
} else {
// If we don't have a TOC, just provide a stub value
debug(5, "AppleCDXObj::m_getFirstFrame(%i): returning default", trackNum);
g_lingo->push(Datum(0));
}
}
// Currently calculated based on the start of the next track, since
// cuesheets don't contain the duration of a song.
void AppleCDXObj::m_getLastFrame(int nargs) {
AppleCDXObject *me = static_cast<AppleCDXObject *>(g_lingo->_state->me.u.obj);
int trackNum = g_lingo->pop().asInt();
if (me->_cue) {
// We look for the pregap of the next track, if there is one.
// TODO opening the actual audio track and getting its length
// in sectors would produce a more accurate result for the final track
Common::CueSheet::CueTrack *track = me->_cue->getTrack(trackNum + 1);
int index;
if (track) {
// Don't use the pregap if there is no pregap
index = track->indices[0] == -1 ? track->indices[1] : track->indices[0];
} else {
debug(5, "AppleCDXObj::m_getLastFrame(%i): no track at trackNum %i, setting index to 0", trackNum, trackNum + 1);
index = 0;
}
debug(5, "AppleCDXObj::m_getLastFrame(%i): returning %i", trackNum, index);
g_lingo->push(Datum(index));
} else {
// If we don't have a TOC, just provide a stub value
debug(5, "AppleCDXObj::m_getLastFrame(%i): returning default", trackNum);
g_lingo->push(Datum(0));
}
}
void AppleCDXObj::m_eject(int nargs) {
debug(5, "AppleCDXObj::eject: Ejecting CD");
}
} // End of namespace Director

View File

@@ -0,0 +1,74 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_XLIBS_APPLECDXOBJ_H
#define DIRECTOR_LINGO_XLIBS_APPLECDXOBJ_H
#include "common/ptr.h"
namespace Common {
class CueSheet;
}
namespace Director {
class AppleCDXObject : public Object<AppleCDXObject> {
public:
AppleCDXObject(ObjectType objType);
int _inpoint;
int _outpoint;
// Instead of immediately returning values, methods which return
// a value store it internally and return it via a subsequent
// call to mGetValue.
int _returnValue;
Common::SharedPtr<Common::CueSheet> _cue;
};
namespace AppleCDXObj {
extern const char *const xlibName;
extern const XlibFileDesc fileNames[];
void open(ObjectType type, const Common::Path &path);
void close(ObjectType type);
void m_new(int nargs);
void m_dispose(int nargs);
void m_still(int nargs);
void m_service(int nargs);
void m_readStatus(int nargs);
void m_getValue(int nargs);
void m_setInPoint(int nargs);
void m_setOutPoint(int nargs);
void m_playCue(int nargs);
void m_playSegment(int nargs);
void m_readPos(int nargs);
void m_getFirstTrack(int nargs);
void m_getLastTrack(int nargs);
void m_getFirstFrame(int nargs);
void m_getLastFrame(int nargs);
void m_eject(int nargs);
} // End of namespace AppleCDXObj
} // End of namespace Director
#endif

View File

@@ -0,0 +1,140 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "director/types.h"
#include "gui/message.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/xlibs/a/askuser.h"
namespace Director {
/**************************************************
*
* USED IN:
* DEVO Presents: Adventures of the Smart Patrol
*
**************************************************/
/*
* -- AskUser XObject. Copyright 1996 Inscape v1.0 24May96 BDL
* AskUser
* I mNew --Creates a new instance
* X mDispose --Disposes XObject instance
* SSSS mAsk --Data to display in the message box.
*/
const char *const AskUser::xlibName = "AskUser";
const XlibFileDesc AskUser::fileNames[] = {
{ "AskUser", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{ "new", AskUser::m_new, 0, 0, 400 }, // D4
{ "ask", AskUser::m_ask, 3, 3, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
AskUserXObject::AskUserXObject(ObjectType ObjectType) :Object<AskUserXObject>("AskUser") {
_objType = ObjectType;
}
void AskUser::open(ObjectType type, const Common::Path &path) {
if (type == kXObj) {
AskUserXObject::initMethods(xlibMethods);
AskUserXObject *xobj = new AskUserXObject(kXObj);
g_lingo->exposeXObject(xlibName, xobj);
}
}
void AskUser::close(ObjectType type) {
if (type == kXObj) {
AskUserXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
}
void AskUser::m_new(int nargs) {
g_lingo->printSTUBWithArglist("AskUser::m_new", nargs);
g_lingo->dropStack(nargs);
g_lingo->push(g_lingo->_state->me);
}
void AskUser::m_ask(int nargs) {
if (nargs != 3) {
warning("AskUser::m_ask: expected 3 arguments");
g_lingo->dropStack(nargs);
g_lingo->push(Datum("Ok"));
return;
}
Datum text = g_lingo->pop();
Datum caption = g_lingo->pop(); // ScummVM doesn't have a title for message boxes, not used
Datum mbType = g_lingo->pop();
if (text.type != STRING) {
warning("AskUser::m_ask: expected text to be a string, not %s", text.type2str());
g_lingo->push(Datum("Ok"));
return;
}
if (mbType.type != STRING) {
warning("AskUser::m_ask: expected mbType to be a string, not %s", mbType.type2str());
g_lingo->push(Datum("Ok"));
return;
}
Common::U32String defaultButton;
Common::U32StringArray altButtons;
if (mbType.u.s->equals("YesNoCancel")) {
defaultButton = Common::U32String("Yes");
altButtons.push_back(Common::U32String("No"));
altButtons.push_back(Common::U32String("Cancel"));
} else if (mbType.u.s->equals("YesNo")) {
defaultButton = Common::U32String("Yes");
altButtons.push_back(Common::U32String("No"));
} else if (mbType.u.s->equals("OkCancel")) {
defaultButton = Common::U32String("OK");
altButtons.push_back(Common::U32String("Cancel"));
} else if (mbType.u.s->equals("Ok")) {
defaultButton = Common::U32String("OK");
} else {
warning("AskUser::m_ask: unhandled mbType %s, falling back to Ok", mbType.u.s->c_str());
defaultButton = Common::U32String("OK");
}
g_director->_wm->clearHandlingWidgets();
GUI::MessageDialog dialog(Common::U32String(text.u.s->c_str()), defaultButton, altButtons);
int result = dialog.runModal();
if (result == GUI::kMessageOK) {
g_lingo->push(Datum(Common::String(defaultButton)));
} else if ((result >= GUI::kMessageAlt) && (result < GUI::kMessageAlt + (int)altButtons.size())) {
g_lingo->push(Datum(Common::String(altButtons[result - GUI::kMessageAlt])));
} else {
warning("AskUser::m_ask: got unexpected dialog result of %d", result);
g_lingo->push(Datum("Ok"));
}
}
}

View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_XLIBS_ASKUSER_H
#define DIRECTOR_LINGO_XLIBS_ASKUSER_H
namespace Director {
class AskUserXObject : public Object<AskUserXObject> {
public:
AskUserXObject(ObjectType objType);
};
namespace AskUser {
extern const char *const xlibName;
extern const XlibFileDesc fileNames[];
void open(ObjectType type, const Common::Path &path);
void close(ObjectType type);
void m_new(int nargs);
void m_dispose(int nargs);
void m_ask(int nargs);
} // End of namespace AskUser
} // End of namespace Director
#endif

View File

@@ -0,0 +1,125 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/b/backdrop.h"
/**************************************************
*
* USED IN:
* A Trip To The Sky
* Die Hexenakademie
*
**************************************************/
/*
-- Backdrop XObject
-- Draws a backdrop behind the Director stage
-- Version 2.0.1, September 27, 1994
-- ©1993-94 Electronic Ink
I mNew
X mDispose
X mShow
X mHide
X mPaint
V mSetColor, index [or] r,g,b
V mSetBgColor, index [or] r,g,b
V mSetPattern, patNum [or] patName
XI mSetPPat, ppatID
V mSetPicture, castPict [or] pictID [or] pictFile
XI mHideInBack, trueOrFalse
XI mHideMessages, trueOrFalse
XS mRegister, serialNumber
*/
namespace Director {
const char *const BackdropXObj::xlibName = "Backdrop";
const XlibFileDesc BackdropXObj::fileNames[] = {
{ "Backdrop", nullptr },
{ "backdrop.obj", nullptr },
{ "Backdrop XObj", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{ "new", BackdropXObj::m_new, 0, 0, 400 },
{ "dispose", BackdropXObj::m_dispose, 0, 0, 400 },
{ "show", BackdropXObj::m_show, 0, 0, 400 },
{ "hide", BackdropXObj::m_hide, 0, 0, 400 },
{ "paint", BackdropXObj::m_paint, 0, 0, 400 },
{ "setColor", BackdropXObj::m_setColor, 0, 0, 400 },
{ "setBgColor", BackdropXObj::m_setBgColor, 0, 0, 400 },
{ "setPattern", BackdropXObj::m_setPattern, 0, 0, 400 },
{ "setPPat", BackdropXObj::m_setPPat, 1, 1, 400 },
{ "setPicture", BackdropXObj::m_setPicture, 0, 0, 400 },
{ "hideInBack", BackdropXObj::m_hideInBack, 1, 1, 400 },
{ "hideMessages", BackdropXObj::m_hideMessages, 1, 1, 400 },
{ "register", BackdropXObj::m_register, 1, 1, 400 },
{ nullptr, nullptr, 0, 0, 0 }
};
static const BuiltinProto xlibBuiltins[] = {
{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
};
BackdropXObject::BackdropXObject(ObjectType ObjectType) :Object<BackdropXObject>("Backdrop") {
_objType = ObjectType;
}
void BackdropXObj::open(ObjectType type, const Common::Path &path) {
BackdropXObject::initMethods(xlibMethods);
BackdropXObject *xobj = new BackdropXObject(type);
g_lingo->exposeXObject(xlibName, xobj);
g_lingo->initBuiltIns(xlibBuiltins);
}
void BackdropXObj::close(ObjectType type) {
BackdropXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
void BackdropXObj::m_new(int nargs) {
g_lingo->printSTUBWithArglist("BackdropXObj::m_new", nargs);
g_lingo->dropStack(nargs);
g_lingo->push(g_lingo->_state->me);
}
XOBJSTUBNR(BackdropXObj::m_dispose)
XOBJSTUBNR(BackdropXObj::m_show)
XOBJSTUBNR(BackdropXObj::m_hide)
XOBJSTUBNR(BackdropXObj::m_paint)
XOBJSTUB(BackdropXObj::m_setColor, 0)
XOBJSTUB(BackdropXObj::m_setBgColor, 0)
XOBJSTUB(BackdropXObj::m_setPattern, 0)
XOBJSTUBNR(BackdropXObj::m_setPPat)
XOBJSTUB(BackdropXObj::m_setPicture, 0)
XOBJSTUBNR(BackdropXObj::m_hideInBack)
XOBJSTUBNR(BackdropXObj::m_hideMessages)
XOBJSTUBNR(BackdropXObj::m_register)
}

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_XLIBS_BACKDROP_H
#define DIRECTOR_LINGO_XLIBS_BACKDROP_H
namespace Director {
class BackdropXObject : public Object<BackdropXObject> {
public:
BackdropXObject(ObjectType objType);
};
namespace BackdropXObj {
extern const char *const xlibName;
extern const XlibFileDesc fileNames[];
void open(ObjectType type, const Common::Path &path);
void close(ObjectType type);
void m_new(int nargs);
void m_dispose(int nargs);
void m_show(int nargs);
void m_hide(int nargs);
void m_paint(int nargs);
void m_setColor(int nargs);
void m_setBgColor(int nargs);
void m_setPattern(int nargs);
void m_setPPat(int nargs);
void m_setPicture(int nargs);
void m_hideInBack(int nargs);
void m_hideMessages(int nargs);
void m_register(int nargs);
} // End of namespace BackdropXObj
} // End of namespace Director
#endif

View File

@@ -0,0 +1,90 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*************************************
*
* USED IN:
* Angel Gate demo
*
*************************************/
/*
* -- Picter, a simple demo XObject, v1.0
* I mNew
* XIII mGpal, h, l, s
* XIIIIII mLine, y1, y2, y3, y4, y5, c
* II mGetDate, value
* X mClear
*/
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/b/barakeobj.h"
namespace Director {
const char *const BarakeObj::xlibName = "BarakeObj";
const XlibFileDesc BarakeObj::fileNames[] = {
{ "BarakeObj", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{ "new", BarakeObj::m_new, 0, 0, 400 }, // D4
{ "Clear", BarakeObj::m_clear, 0, 0, 400 }, // D4
{ "Gpal", BarakeObj::m_gpal, 3, 3, 400 }, // D4
{ "Line", BarakeObj::m_line, 6, 6, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
void BarakeObj::open(ObjectType type, const Common::Path &path) {
if (type == kXObj) {
BarakeObject::initMethods(xlibMethods);
BarakeObject *xobj = new BarakeObject(kXObj);
g_lingo->exposeXObject(xlibName, xobj);
}
}
void BarakeObj::close(ObjectType type) {
if (type == kXObj) {
BarakeObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
}
BarakeObject::BarakeObject(ObjectType ObjectType) :Object<BarakeObject>("BarakeObj") {
_objType = ObjectType;
}
void BarakeObj::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
void BarakeObj::m_clear(int nargs) {
}
XOBJSTUBNR(BarakeObj::m_gpal)
XOBJSTUBNR(BarakeObj::m_line)
} // End of namespace Director

View File

@@ -0,0 +1,49 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_XLIBS_BARAKEOBJ_H
#define DIRECTOR_LINGO_XLIBS_BARAKEOBJ_H
namespace Director {
class BarakeObject : public Object<BarakeObject> {
public:
BarakeObject(ObjectType objType);
};
namespace BarakeObj {
extern const char *const xlibName;
extern const XlibFileDesc fileNames[];
void open(ObjectType type, const Common::Path &path);
void close(ObjectType type);
void m_new(int nargs);
void m_clear(int nargs);
void m_gpal(int nargs);
void m_line(int nargs);
} // End of namespace BarakeObj
} // End of namespace Director
#endif

View File

@@ -0,0 +1,281 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*************************************
*
* USED IN:
* teamxtreme1-win
* teamxtreme2-win
*
*************************************/
/*
* -- BatQt quicktime factory. 9Aug94 RNB
* BatQt
* I mNew --Creates a new instance of the XObject
* X mDispose --Disposes of XObject instance
* S mName --Returns the XObject name
* I mStatus --Returns an integer status code
* SI mError, code --Returns an error string
* S mLastError --Returns last error string
* ISI mOpen --Opens the specified movie
* IIIS mPlay --Play the movie, after setting parms
* I mStop --Stop the movie
* S mGetTimeRange --Gets the current time range
* S mGetMovieBox --Gets the current bounds box of the movie
* I mGetTime --Gets the current time of the movie
* SI mSetTime --Sets the current time of the movie
* SI mSetVolume --Sets the volume of the movie
* I mLength --Gets the length of the movie
* IIIII mSetMovieBox --Sets the bounding box of the movie
* III mSetTimeRange -- Sets the active segment of the movie
* II mAddCallback -- Adds a callback for the movie
* II mRemoveCallback -- Removes a callback for the movie
* I mResetCallbacks -- Resets the sent status of the callbacks
* XS mSetBatch -- Applies a set of batch commands
*/
#include "graphics/paletteman.h"
#include "video/qt_decoder.h"
#include "director/director.h"
#include "director/util.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/b/batqt.h"
namespace Director {
// The name is different from the obj filename.
const char *const BatQT::xlibName = "batQT";
const XlibFileDesc BatQT::fileNames[] = {
{ "batQT", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{ "new", BatQT::m_new, 0, 0, 400 }, // D4
{ "dispose", BatQT::m_dispose, 1, 1, 400 }, // D4
{ "name", BatQT::m_name, 0, 0, 400 }, // D4
{ "status", BatQT::m_status, 0, 0, 400 }, // D4
{ "error", BatQT::m_error, 1, 1, 400 }, // D4
{ "lastError", BatQT::m_lastError, 0, 0, 400 }, // D4
{ "open", BatQT::m_open, 2, 2, 400 }, // D4
{ "play", BatQT::m_play, 3, 3, 400 }, // D4
{ "stop", BatQT::m_stop, 0, 0, 400 }, // D4
{ "getTimeRange", BatQT::m_getTimeRange, 0, 0, 400 }, // D4
{ "getMovieBox", BatQT::m_getMovieBox, 0, 0, 400 }, // D4
{ "getTime", BatQT::m_getTime, 0, 0, 400 }, // D4
{ "setTime", BatQT::m_setTime, 1, 1, 400 }, // D4
{ "setVolume", BatQT::m_setVolume, 1, 1, 400 }, // D4
{ "length", BatQT::m_length, 0, 0, 400 }, // D4
{ "setMovieBox", BatQT::m_setMovieBox, 4, 4, 400 }, // D4
{ "setTimeRange", BatQT::m_setTimeRange, 2, 2, 400 }, // D4
{ "addCallback", BatQT::m_addCallback, 1, 1, 400 }, // D4
{ "removeCallback", BatQT::m_removeCallback,1, 1, 400 }, // D4
{ "resetCallbacks", BatQT::m_resetCallbacks,0, 0, 400 }, // D4
{ "setBatch", BatQT::m_setBatch, 1, 1, 400 }, // D4
{ nullptr, nullptr, 0, 0, 0 }
};
void BatQT::open(ObjectType type, const Common::Path &path) {
if (type == kXObj) {
BatQTXObject::initMethods(xlibMethods);
BatQTXObject *xobj = new BatQTXObject(kXObj);
g_lingo->exposeXObject(xlibName, xobj);
}
}
void BatQT::close(ObjectType type) {
if (type == kXObj) {
BatQTXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
}
BatQTXObject::BatQTXObject(ObjectType ObjectType) : Object<BatQTXObject>("BatQt") {
_objType = ObjectType;
_video = nullptr;
}
BatQTXObject::~BatQTXObject() {
if (_video) {
delete _video;
_video = nullptr;
}
}
void BatQT::m_new(int nargs) {
g_lingo->push(g_lingo->_state->me);
}
void BatQT::m_dispose(int nargs) {
debug(5, "BatQT::m_dispose");
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
if (me->_video) {
delete me->_video;
me->_video = nullptr;
}
}
XOBJSTUB(BatQT::m_name, "")
XOBJSTUB(BatQT::m_status, 0)
XOBJSTUB(BatQT::m_error, "")
XOBJSTUB(BatQT::m_lastError, "")
void BatQT::m_open(int nargs) {
ARGNUMCHECK(2);
Datum unk = g_lingo->pop();
Datum path = g_lingo->pop();
TYPECHECK(path, STRING);
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
Common::Path normPath = findPath(path.asString());
if (!normPath.empty()) {
me->_video = new Video::QuickTimeDecoder();
debugC(5, kDebugXObj, "BatQT::m_open: Loading QT file %s", normPath.toString().c_str());
if (me->_video->loadFile(normPath)) {
me->_movieBox = Common::Rect(me->_video->getWidth(), me->_video->getHeight());
if (g_director->_pixelformat.bytesPerPixel == 1) {
// Director supports playing back RGB and paletted video in 256 colour mode.
// In both cases they are dithered to match the Director palette.
me->_video->setDitheringPalette(g_director->getPalette());
}
} else {
warning("BatQT::m_open: Could not load QT file %s", normPath.toString().c_str());
}
} else {
warning("BatQT::m_open: Could not resolve path %s", path.asString().c_str());
}
g_lingo->push(0);
}
void BatQT::m_play(int nargs) {
ARGNUMCHECK(3);
Datum unk3 = g_lingo->pop();
Datum unk2 = g_lingo->pop();
Datum unk1 = g_lingo->pop();
TYPECHECK(unk1, INT);
TYPECHECK(unk2, INT);
TYPECHECK(unk3, STRING);
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
if (me->_video) {
debugC(5, kDebugXObj, "BatQT::m_play: Starting playback");
me->_video->start();
} else {
warning("BatQT::m_play: No video loaded");
}
g_lingo->push(0);
}
void BatQT::m_stop(int nargs) {
ARGNUMCHECK(0);
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
if (me->_video) {
debugC(5, kDebugXObj, "BatQT::m_stop: Stopping playback");
me->_video->stop();
} else {
warning("BatQT::m_stop: No video loaded");
}
g_lingo->push(0);
}
XOBJSTUB(BatQT::m_getTimeRange, "")
void BatQT::m_getMovieBox(int nargs) {
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
Common::String result = Common::String::format(
"%d,%d,%d,%d",
me->_movieBox.left,
me->_movieBox.top,
me->_movieBox.width(),
me->_movieBox.height()
);
debugC(5, kDebugXObj, "BatQT::m_getMovieBox: %s", result.c_str());
g_lingo->push(result);
}
void BatQT::m_getTime(int nargs) {
ARGNUMCHECK(0);
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
Datum result(0);
if (me->_video) {
// Game uses a polling loop of m_getTime to measure progress,
// therefore we need to render the frames in here
if (me->_video->needsUpdate()) {
const Graphics::Surface *frame = me->_video->decodeNextFrame();
if (frame) {
Graphics::Surface *temp = frame->scale(me->_movieBox.width(), me->_movieBox.height(), false);
g_system->copyRectToScreen(temp->getPixels(), temp->pitch, me->_movieBox.left, me->_movieBox.top, temp->w, temp->h);
g_system->updateScreen();
delete temp;
}
}
result = Datum(me->_video->getCurFrame() + 1);
debugC(5, kDebugXObj, "BatQT::m_getTime: %d", result.asInt());
} else {
warning("BatQT::m_getTime: No video loaded");
}
g_lingo->push(result);
}
XOBJSTUB(BatQT::m_setTime, "")
XOBJSTUB(BatQT::m_setVolume, "")
void BatQT::m_length(int nargs) {
ARGNUMCHECK(0);
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
Datum result(0);
if (me->_video) {
result = Datum((int)me->_video->getFrameCount());
debugC(5, kDebugXObj, "BatQT::m_length: %d", result.asInt());
}
g_lingo->push(result);
}
void BatQT::m_setMovieBox(int nargs) {
ARGNUMCHECK(4);
Datum h = g_lingo->pop();
Datum w = g_lingo->pop();
Datum y = g_lingo->pop();
Datum x = g_lingo->pop();
TYPECHECK(h, INT);
TYPECHECK(w, INT);
TYPECHECK(y, INT);
TYPECHECK(x, INT);
BatQTXObject *me = static_cast<BatQTXObject *>(g_lingo->_state->me.u.obj);
me->_movieBox.left = x.asInt();
me->_movieBox.top = y.asInt();
me->_movieBox.setWidth(w.asInt());
me->_movieBox.setHeight(h.asInt());
debugC(5, kDebugXObj, "BatQT::m_setMovieBox: %d,%d,%d,%d", me->_movieBox.left, me->_movieBox.top, me->_movieBox.width(), me->_movieBox.height());
g_lingo->push(0);
}
XOBJSTUB(BatQT::m_setTimeRange, 0)
XOBJSTUB(BatQT::m_addCallback, 0)
XOBJSTUB(BatQT::m_removeCallback, 0)
XOBJSTUB(BatQT::m_resetCallbacks, 0)
XOBJSTUBNR(BatQT::m_setBatch)
} // End of namespace Director

View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DIRECTOR_LINGO_XLIBS_BATQT_H
#define DIRECTOR_LINGO_XLIBS_BATQT_H
#include "common/rect.h"
namespace Video {
class QuickTimeDecoder;
}
namespace Director {
class BatQTXObject : public Object<BatQTXObject> {
public:
BatQTXObject(ObjectType objType);
~BatQTXObject();
Video::QuickTimeDecoder *_video;
Common::Rect _movieBox;
};
namespace BatQT {
extern const char *const xlibName;
extern const XlibFileDesc fileNames[];
void open(ObjectType type, const Common::Path &path);
void close(ObjectType type);
void m_new(int nargs);
void m_dispose(int nargs);
void m_name(int nargs);
void m_status(int nargs);
void m_error(int nargs);
void m_lastError(int nargs);
void m_open(int nargs);
void m_play(int nargs);
void m_stop(int nargs);
void m_getTimeRange(int nargs);
void m_getMovieBox(int nargs);
void m_getTime(int nargs);
void m_setTime(int nargs);
void m_setVolume(int nargs);
void m_length(int nargs);
void m_setMovieBox(int nargs);
void m_setTimeRange(int nargs);
void m_addCallback(int nargs);
void m_removeCallback(int nargs);
void m_resetCallbacks(int nargs);
void m_setBatch(int nargs);
} // End of namespace BatQT
} // End of namespace Director
#endif

View File

@@ -0,0 +1,97 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "director/director.h"
#include "director/lingo/lingo.h"
#include "director/lingo/lingo-object.h"
#include "director/lingo/lingo-utils.h"
#include "director/lingo/xlibs/b/bimxobj.h"
/**************************************************
*
* USED IN:
* Karma: Curse of the 12 Caves
*
**************************************************/
/*
* -- BIM16 External Factory. 14JULY94
* --BIM
* I mNew --Creates a new instance of the XObject
* ISII mPlay, FileName, x, y --Play Flc file
* ISIII mPlayTo, FileName, x, y, frame --Play Flc file
* ISIII mVideo, FileName, x, y, delay --Play Flc file
* ISIIII mStretch, FileName, destx, desty, destw, desth --Play Flc file
* I mDispose --Disposes of XObject instance
*/
namespace Director {
const char *const BIMXObj::xlibName = "BIM";
const XlibFileDesc BIMXObj::fileNames[] = {
{ "FLC", nullptr },
{ "BIM", nullptr },
{ nullptr, nullptr },
};
static const MethodProto xlibMethods[] = {
{"new", BIMXObj::m_new, 0, 0, 400},
{"play", BIMXObj::m_play, 3, 3, 400},
{"playTo", BIMXObj::m_playTo, 4, 4, 400},
{"video", BIMXObj::m_video, 4, 4, 400},
{"stretch", BIMXObj::m_stretch, 5, 5, 400},
{nullptr, nullptr, 0, 0, 0}
};
static const BuiltinProto xlibBuiltins[] = {
{ nullptr, nullptr, 0, 0, 0, VOIDSYM }
};
BIMXObject::BIMXObject(const ObjectType objType) :Object<BIMXObject>("BIM") {
_objType = objType;
}
void BIMXObj::open(ObjectType type, const Common::Path &path) {
BIMXObject::initMethods(xlibMethods);
BIMXObject *xobj = new BIMXObject(type);
g_lingo->exposeXObject(xlibName, xobj);
g_lingo->initBuiltIns(xlibBuiltins);
}
void BIMXObj::close(ObjectType type) {
BIMXObject::cleanupMethods();
g_lingo->_globalvars[xlibName] = Datum();
}
void BIMXObj::m_new(const int nargs) {
g_lingo->printSTUBWithArglist("BIMXObj::m_new", nargs);
g_lingo->dropStack(nargs);
g_lingo->push(g_lingo->_state->me);
}
XOBJSTUB(BIMXObj::m_play, 0)
XOBJSTUB(BIMXObj::m_playTo, 0)
XOBJSTUB(BIMXObj::m_video, 0)
XOBJSTUB(BIMXObj::m_stretch, 0)
}

Some files were not shown because too many files have changed in this diff Show More