-
Notifications
You must be signed in to change notification settings - Fork 2
/
Mrifk_storyfile.hs
177 lines (134 loc) · 4.44 KB
/
Mrifk_storyfile.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
{-
Mrifk, a decompiler for Glulx story files.
Copyright 2004 Ben Rudiak-Gould.
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 2
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 can read the GNU General Public License at this URL:
http://www.gnu.org/copyleft/gpl.html
-}
module Mrifk_storyfile (
wholeFile,
byteAt, dwordAt, bytesFrom,
getPos, isEOS, peekUByte, getUByte, getBytes,
getSByte, getUWord, getSWord, getDword,
repeatUntilEmpty, evalFrom, evalFromTo, fromTo,
hdrMagic, hdrVersion, hdrRAMStart,
hdrExtStart, hdrStartFunc, hdrDecodingTbl
) where
import qualified ReadBinary
import Mrifk_util
import Mrifk_cmdline
import System.IO.Unsafe (unsafePerformIO)
import Control.Monad.State (State,get,put,evalState)
import Foreign (plusPtr)
(inputFile,inputFileLen) =
handleBlorb $ unsafePerformIO $
ReadBinary.readBinaryFile storyFileName
byteAt n = ReadBinary.byteAt inputFile n
wholeFile :: DataBlock
wholeFile = (0,inputFileLen)
from :: Int -> DataBlock
from n = onFst (+n) wholeFile
fromTo :: Int -> Int -> DataBlock
fromTo n k = (n,k)
evalFrom n act = evalState act (from n)
evalFromTo n k act = evalState act (fromTo n k)
dwordAt n = evalFrom n getDword
bytesFrom n = evalFrom n (repeatUntilEmpty getUByte)
{-----------}
hdrMagic = dwordAt 0 -- "Glul"
hdrVersion = dwordAt 4
hdrRAMStart = dwordAt 8
hdrExtStart = dwordAt 12
hdrEndMem = dwordAt 16
hdrStackSize = dwordAt 20
hdrStartFunc = dwordAt 24
hdrDecodingTbl = dwordAt 28
hdrExtensionMagic = dwordAt 36 -- "Info"
hdrExtensionFormatVersion = dwordAt 40
hdrExtensionInformVersion = dwordAt 44
{-------------}
handleBlorb :: (ReadBinary.BinaryData, Int)
-> (ReadBinary.BinaryData, Int)
handleBlorb (p,size) =
if dwordAt 0 /= 0x464F524D then
(p,size)
else if dwordAt 8 /= 0x49465253 || dwordAt 12 /= 0x52496478 then
error "Unrecognized blorb file format"
else
let numResources = dwordAt 16
resources = take numResources
[(dwordAt n, dwordAt (n+8)) | n <- [24,36..]]
in case [pos | (0x45786563,pos) <- resources] of
[] -> error "No story file in blorb"
[pos] -> case dwordAt pos of
0x5A434F44 -> error "This appears to be a Z-machine blorb. Try Reform."
0x474C554C -> (p `plusPtr` (pos+8), dwordAt (pos+4))
_ -> error "Unrecognized blorb file format"
_ -> error "More than one story file found. You'll have to extract one by hand."
where
byteAt n = ReadBinary.byteAt p n
dwordAt n = byteAt n * 16777216
+ byteAt (n+1) * 65536
+ byteAt (n+2) * 256
+ byteAt (n+3)
{----------}
type DataBlock = (Int,Int)
type StreamReader a = State DataBlock a
getPos :: StreamReader Int
getPos =
do (a,z) <- get
return a
isEOS :: StreamReader Bool
isEOS =
do (a,z) <- get
return (a >= z)
peekUByte :: StreamReader Int
peekUByte =
do (a,z) <- get
return (byteAt a)
getUByte :: StreamReader Int
getUByte =
do (a,z) <- get
put (a+1,z) -- should probably bounds-check
return (byteAt a)
getBytes :: Int -> StreamReader DataBlock
getBytes n =
do (a,z) <- get
put (a+n,z) -- definitely bounds-check
return (a,a+n)
getSByte :: StreamReader Int
getSByte =
do x <- getUByte
return (if x < 128 then x else x - 256)
getUWord :: StreamReader Int
getUWord =
do a <- getUByte
b <- getUByte
return (a*256+b)
getSWord :: StreamReader Int
getSWord =
do a <- getSByte
b <- getUByte
return (a*256+b)
getDword :: StreamReader Int
getDword =
do hi <- getSWord
lo <- getUWord
return (hi*65536+lo)
repeatUntilEmpty :: StreamReader a -> StreamReader [a]
repeatUntilEmpty action =
do eos <- isEOS
if eos then
return []
else do
first <- action
s <- get
let lazyRest = evalState (repeatUntilEmpty action) s
return (first : lazyRest)