gruesome 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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