gruesome 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,337 @@
1
+ module Gruesome
2
+ module Z
3
+
4
+ # This translates between the special ZSCII character encoding
5
+ # to ASCII / UNICODE
6
+ module ZSCII
7
+
8
+ ALPHABET_TABLES = [
9
+ "abcdefghijklmnopqrstuvwxyz",
10
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
11
+ " \n0123456789.,!?_#'\"/\\-:()"
12
+ ]
13
+
14
+ V1_A2 = " 0123456789.,!?_#'\"/\\<-:()"
15
+
16
+ UNICODE_TRANSLATION_TABLE = {
17
+ 155 => "\u00e4", 156 => "\u00f6", 157 => "\u00fc", 158 => "\u00c4", 159 => "\u00d6",
18
+ 160 => "\u00dc", 161 => "\u00df", 162 => "\u00bb", 163 => "\u00ab", 164 => "\u00eb",
19
+ 165 => "\u00ef", 166 => "\u00ff", 167 => "\u00cb", 168 => "\u00cf", 169 => "\u00e1",
20
+ 170 => "\u00e9", 171 => "\u00ed", 172 => "\u00f3", 173 => "\u00fa", 174 => "\u00fd",
21
+ 175 => "\u00c1", 176 => "\u00c9", 177 => "\u00cd", 178 => "\u00d3", 179 => "\u00da",
22
+ 180 => "\u00dd", 181 => "\u00e0", 182 => "\u00e8", 183 => "\u00ec", 184 => "\u00f2",
23
+ 185 => "\u00f9", 186 => "\u00c0", 187 => "\u00c8", 188 => "\u00cc", 189 => "\u00d2",
24
+ 190 => "\u00d9", 191 => "\u00e2", 192 => "\u00ea", 193 => "\u00ee", 194 => "\u00f4",
25
+ 195 => "\u00fb", 196 => "\u00c2", 197 => "\u00ca", 198 => "\u00ce", 199 => "\u00d4",
26
+ 200 => "\u00db", 201 => "\u00e5", 202 => "\u00c5", 203 => "\u00f8", 204 => "\u00d8",
27
+ 205 => "\u00e3", 206 => "\u00f1", 207 => "\u00f5", 208 => "\u00c3", 209 => "\u00d1",
28
+ 210 => "\u00d5", 211 => "\u00e6", 212 => "\u00c6", 213 => "\u00e7", 214 => "\u00c7",
29
+ 215 => "\u00fe", 216 => "\u00f0", 217 => "\u00de", 218 => "\u00d0", 219 => "\u00a3",
30
+ 220 => "\u0153", 221 => "\u0152", 222 => "\u00a1", 223 => "\u00bf"
31
+ }
32
+
33
+ # figure out if a shift lock is pressed and return it
34
+ def ZSCII.eval_alphabet(initial_alphabet, version, codes)
35
+ if version < 3
36
+ else
37
+ initial_alphabet
38
+ end
39
+ end
40
+
41
+ def ZSCII.translate_Zchar(zchar)
42
+ if zchar == 9
43
+ "\t"
44
+ elsif zchar == 11
45
+ " "
46
+ elsif zchar == 13
47
+ "\n"
48
+ elsif zchar >= 32 and zchar <= 126
49
+ zchar.chr
50
+ elsif zchar >= 155 and zchar <= 223
51
+ UNICODE_TRANSLATION_TABLE[zchar]
52
+ end
53
+ end
54
+
55
+ def ZSCII.translate_char_to_zchar(chr, version)
56
+ if chr == "\t"
57
+ 9
58
+ elsif chr == " "
59
+ 11
60
+ elsif chr == "\n"
61
+ 13
62
+ elsif chr.getbyte(0) >= 32 and chr.getbyte(0) <= 126
63
+ chr.getbyte(0)
64
+ else
65
+ # XXX: Unicode Translation Table reversed
66
+ "?".getbyte(0)
67
+ end
68
+ end
69
+
70
+ # Give array of Z-codes
71
+ def ZSCII.translate_char(chr, version)
72
+ ret = []
73
+ if chr >= 'a'[0] and chr <= 'z'[0]
74
+ # A0
75
+ chr = chr.getbyte(0) - 'a'.getbyte(0)
76
+ chr += 6
77
+ ret << chr
78
+ elsif chr >= 'A'[0] and chr <= 'Z'[0]
79
+ # A1
80
+ chr = chr.getbyte(0) - 'A'.getbyte(0)
81
+ chr += 6
82
+
83
+ ret << 4
84
+ ret << chr
85
+ elsif chr == " "
86
+ ret << 0
87
+ elsif chr == "\n"
88
+ if version == 1
89
+ # use 10-bit code since newline is not in the alphabet 2
90
+ ret << 5
91
+ ret << 6
92
+ ret << 0
93
+ ret << 13
94
+ else
95
+ ret << 5
96
+ ret << 7
97
+ end
98
+ elsif chr >= '0'[0] and chr <= '9'[0]
99
+ chr = chr[0] - '0'[0]
100
+ if version == 1
101
+ chr += 7
102
+ else
103
+ chr += 8
104
+ end
105
+ ret << 5
106
+ ret << chr
107
+ elsif chr == "."
108
+ ret << 5
109
+ if version == 1
110
+ ret << 17
111
+ else
112
+ ret << 18
113
+ end
114
+ elsif chr == ","
115
+ ret << 5
116
+ if version == 1
117
+ ret << 18
118
+ else
119
+ ret << 19
120
+ end
121
+ elsif chr == "!"
122
+ ret << 5
123
+ if version == 1
124
+ ret << 19
125
+ else
126
+ ret << 20
127
+ end
128
+ elsif chr == "?"
129
+ ret << 5
130
+ if version == 1
131
+ ret << 20
132
+ else
133
+ ret << 21
134
+ end
135
+ elsif chr == "_"
136
+ ret << 5
137
+ if version == 1
138
+ ret << 21
139
+ else
140
+ ret << 22
141
+ end
142
+ elsif chr == "#"
143
+ ret << 5
144
+ if version == 1
145
+ ret << 22
146
+ else
147
+ ret << 23
148
+ end
149
+ elsif chr == "'"
150
+ ret << 5
151
+ if version == 1
152
+ ret << 23
153
+ else
154
+ ret << 24
155
+ end
156
+ elsif chr == "\""
157
+ ret << 5
158
+ if version == 1
159
+ ret << 24
160
+ else
161
+ ret << 25
162
+ end
163
+ elsif chr == "/"
164
+ ret << 5
165
+ if version == 1
166
+ ret << 25
167
+ else
168
+ ret << 26
169
+ end
170
+ elsif chr == "\\"
171
+ ret << 5
172
+ if version == 1
173
+ ret << 26
174
+ else
175
+ ret << 27
176
+ end
177
+ elsif chr == "<"
178
+ if version == 1
179
+ ret << 5
180
+ ret << 27
181
+ else
182
+ # use 10-bit
183
+ ret << 5
184
+ ret << 6
185
+ ret << 0
186
+ ret << '<'[0]
187
+ end
188
+ elsif chr == "-"
189
+ ret << 5
190
+ ret << 28
191
+ elsif chr == ":"
192
+ ret << 5
193
+ ret << 29
194
+ elsif chr == "("
195
+ ret << 5
196
+ ret << 30
197
+ elsif chr == ")"
198
+ ret << 5
199
+ ret << 30
200
+ end
201
+ # TODO: Unicode
202
+
203
+ ret
204
+ end
205
+
206
+ def ZSCII.encode_to_zchars(string, version)
207
+ codes = []
208
+ string.each_char do |chr|
209
+ codes << ZSCII.translate_char_to_zchar(chr, version)
210
+ end
211
+
212
+ codes
213
+ end
214
+
215
+ def ZSCII.encode(string, version)
216
+ codes = []
217
+ string.each_char do |chr|
218
+ subcodes = ZSCII.translate_char(chr, version)
219
+ subcodes.each do |code|
220
+ codes << code
221
+ end
222
+ end
223
+
224
+ # pad codes to be divisible by three
225
+ # and pad with '5' shift code
226
+ (codes.length % 3).times do
227
+ codes << 5
228
+ end
229
+
230
+ # encode the z-codes
231
+ words = []
232
+
233
+ word = 0
234
+ codes.each_with_index do |code, i|
235
+ if (i % 3) == 0
236
+ word = (code & 0b11111) << 10
237
+ elsif (i % 3) == 1
238
+ word |= (code & 0b11111) << 5
239
+ else
240
+ word |= code & 0b11111
241
+ words << word
242
+ end
243
+ end
244
+
245
+ # Give single that it is the last word
246
+ words[-1] |= 0b10000000_00000000
247
+
248
+ words
249
+ end
250
+
251
+ # return the utf8 string for the given ZSCII code
252
+ def ZSCII.translate(initial_alphabet, version, zscii_str, abbreviation_table = nil, table = nil)
253
+ str = ""
254
+
255
+ alphabet = initial_alphabet
256
+
257
+ # for abbreviations
258
+ next_is_abbrev = false
259
+ abbrev_alphabet = 0
260
+
261
+ # for 10-bit characters
262
+ next_is_big_char_top = false
263
+ next_is_big_char_bottom = false
264
+ big_char = 0
265
+
266
+ zscii_str.each do |c|
267
+ if next_is_big_char_top
268
+ next_is_big_char_top = false
269
+ next_is_big_char_bottom = true
270
+
271
+ big_char = c
272
+ elsif next_is_big_char_bottom
273
+ next_is_big_char_bottom = false
274
+
275
+ big_char = big_char << 5
276
+ big_char |= c
277
+
278
+ str += translate_Zchar(big_char)
279
+ elsif next_is_abbrev
280
+ next_is_abbrev = false
281
+
282
+ str += abbreviation_table.lookup(abbrev_alphabet, c, 0)
283
+ elsif (version < 3 and c == 2) or c == 4
284
+ # handle shift characters
285
+ alphabet = (alphabet + 1) % 3
286
+ if version < 3 and c == 2
287
+ initial_alphabet = alphabet
288
+ end
289
+ elsif (version < 3 and c == 3) or c == 5
290
+ # handle shift characters
291
+ alphabet = (alphabet - 1) % 3
292
+ if version < 3 and c == 3
293
+ initial_alphabet = alphabet
294
+ end
295
+ elsif (version >= 2 and c == 1) or (version >= 3 and (c == 2 or c == 3))
296
+ # the next code will decide the character to pull from the table
297
+ if abbreviation_table != nil
298
+ next_is_abbrev = true
299
+ abbrev_alphabet = c - 1
300
+ end
301
+ else
302
+ # translate character
303
+ if alphabet == 2 and c == 6
304
+ # 10 bit ZSCII code follows
305
+ # next c gives top 5 bits
306
+ # c after that gives bottom 5 bits
307
+ next_is_big_char_top = true
308
+ elsif c == 0
309
+ # space
310
+ str += " "
311
+ elsif version == 1 and c == 1
312
+ # newline
313
+ str += "\n"
314
+ elsif version == 1 and alphabet == 2
315
+ alphabet_char_idx = c - 6
316
+ str += V1_A2[alphabet_char_idx]
317
+ elsif version <= 4 or table == nil
318
+ # normal translation
319
+ # c will always be > 5 here
320
+ alphabet_char_idx = c - 6
321
+ str += ALPHABET_TABLES[alphabet][alphabet_char_idx]
322
+ elsif version >= 5
323
+ # table is not nil, use it to determine the letter
324
+ # XXX: Use included table (array of three strings)
325
+ end
326
+ end
327
+
328
+ if c < 2 or c > 5
329
+ alphabet = initial_alphabet
330
+ end
331
+ end
332
+
333
+ return str
334
+ end
335
+ end
336
+ end
337
+ end
data/lib/gruesome.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative 'gruesome/machine'
2
+
3
+ module Gruesome
4
+ def Gruesome.execute(story_file)
5
+ Machine.new(story_file).execute
6
+ end
7
+ end
@@ -0,0 +1,90 @@
1
+ require_relative '../../lib/gruesome/z/memory'
2
+
3
+ describe Gruesome::Z::Memory do
4
+ before(:each) do
5
+ zork = File.open('test/zork1.z3', 'r')
6
+ @zork_memory = Gruesome::Z::Memory.new(zork.read(zork.size))
7
+ end
8
+
9
+ describe "#readb" do
10
+ it "should read a byte in header" do
11
+ @zork_memory.readb(0x0).should eql(3)
12
+ end
13
+
14
+ it "should read a byte in dynamic memory" do
15
+ @zork_memory.readb(0x100).should eql(32)
16
+ end
17
+
18
+ it "should read a byte in static memory" do
19
+ @zork_memory.readb(0x2E53).should eql(47)
20
+ @zork_memory.readb(0x4E36).should eql(0)
21
+ end
22
+
23
+ it "should not read a byte in high memory" do
24
+ lambda {@zork_memory.readb(0x4E37)}.should raise_error(RuntimeError)
25
+ end
26
+ end
27
+
28
+ describe "#readw" do
29
+ it "should read a word in header" do
30
+ @zork_memory.readw(0x0).should eql(768)
31
+ end
32
+
33
+ it "should read a word in dynamic memory" do
34
+ @zork_memory.readw(0x100).should eql(8403)
35
+ end
36
+
37
+ it "should read a word in static memory" do
38
+ @zork_memory.readw(0x2E53).should eql(12127)
39
+ @zork_memory.readw(0x4E35).should eql(256)
40
+ end
41
+
42
+ it "should not read a word in high memory" do
43
+ lambda{@zork_memory.readw(0x4E36)}.should raise_error(RuntimeError)
44
+ end
45
+ end
46
+
47
+ describe "#writeb" do
48
+ it "should write a byte in dynamic memory" do
49
+ @zork_memory.writeb(0x100, 128)
50
+ @zork_memory.readb(0x100).should eql(128)
51
+ end
52
+
53
+ it "should not write a byte in static memory" do
54
+ @zork_memory.force_writeb(0x2E53, 123)
55
+ lambda{@zork_memory.writeb(0x2E53, 128)}.should raise_error(RuntimeError)
56
+ @zork_memory.readb(0x2E53).should eql(123)
57
+ @zork_memory.force_writeb(0x4E36, 123)
58
+ lambda{@zork_memory.writeb(0x4E36, 128)}.should raise_error(RuntimeError)
59
+ @zork_memory.readb(0x4E36).should eql(123)
60
+ end
61
+
62
+ it "should not write a byte in high memory" do
63
+ @zork_memory.force_writeb(0x4E37, 123)
64
+ lambda{@zork_memory.writeb(0x4E37, 128)}.should raise_error(RuntimeError)
65
+ @zork_memory.force_readb(0x4E37).should eql(123)
66
+ end
67
+ end
68
+
69
+ describe "#writew" do
70
+ it "should write a word in dynamic memory" do
71
+ @zork_memory.writew(0x100, 12345)
72
+ @zork_memory.readw(0x100).should eql(12345)
73
+ end
74
+
75
+ it "should not write a word in static memory" do
76
+ @zork_memory.force_writew(0x2E53, 23456)
77
+ lambda{@zork_memory.writew(0x2E53, 12345)}.should raise_error(RuntimeError)
78
+ @zork_memory.readw(0x2E53).should eql(23456)
79
+ @zork_memory.force_writew(0x4E35, 23456)
80
+ lambda{@zork_memory.writew(0x4E35, 12345)}.should raise_error(RuntimeError)
81
+ @zork_memory.readw(0x4E35).should eql(23456)
82
+ end
83
+
84
+ it "should not write a word in high memory" do
85
+ @zork_memory.force_writew(0x4E36, 23456)
86
+ lambda{@zork_memory.writew(0x4E36, 12345)}.should raise_error(RuntimeError)
87
+ @zork_memory.force_readw(0x4E36).should eql(23456)
88
+ end
89
+ end
90
+ end