Turns out the palette is actually identical to that in wh40k.pcx

This commit is contained in:
2018-03-25 00:18:27 +00:00
parent 285ae5d292
commit c8238f1853
12 changed files with 959 additions and 13 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
/investigation
/loader
/orig
/view-obj

View File

@@ -220,8 +220,10 @@ be 128x96, or 12288 bytes at 8bpp.
It seems pixels can be larger than a cell - TZEENTCH.OBJ is almost 2 cells high,
for instance.
0x002-0x003 changes in step with total number of pixels, but that doesn't seem
to account for the difference.
Loading a constructed `palette.obj` containing 1x63 pixels, with 0x0000 for the
first 32 bits, caused WH40K_TD.exe to render the pixels far to the left and above
the cell the palette objects were being placed into. This suggests they
represent an x,y offset to draw to. Need to experiment more.
Considering sprites with a 1,1 x,y.

View File

@@ -1,15 +1,12 @@
package data
import (
"image/color"
)
import "image/color"
var (
// From Pic/wh40k.pcx. I didn't feel like implementing the read-in for this.
// Not yet, anyway.
// TODO: at least one of these colours is actually transparent.
Transparent = color.RGBA{R: 0, G: 0, B: 0, A: 0}
ColorPalette = color.Palette{
color.RGBA{R: 0, G: 0, B: 0, A: 255},
Transparent,
color.RGBA{R: 128, G: 0, B: 0, A: 255},
color.RGBA{R: 0, G: 128, B: 0, A: 255},
color.RGBA{R: 128, G: 128, B: 0, A: 255},

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env ruby
lines = File.read("palette.ppm").split("\n")
lines = lines.select { |l| l[0] != "#" }
raise "Not an ASCII ppm" if lines.shift != "P3"
raise "Incorrect dimensions" unless lines.shift == "1 256"
raise "Incorrect maxval" unless lines.shift == "255"
raise "Too many lines left" unless lines.size == 768
puts <<EOF
package data
import "image/color"
var (
Transparent = color.RGBA{R: 0, G: 0, B: 0, A: 0}
ColorPalette = color.Palette{
Transparent,
EOF
lines.shift(3) # Ignore idx 0
255.times do
r, g, b = lines.shift(3).map(&:to_i)
puts "\t\tcolor.RGBA{R: #{r}, G: #{g}, B: #{b}, A: 255},"
end
puts <<EOF
}
)
EOF

View File

@@ -0,0 +1,12 @@
# jungle floor
# jungtil.obj/.asn
# /--> d:\warflics\missions\jungtil.flc
#
0:DEF 2;
0:TYPE 2;
END OF FILE

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,772 @@
P3
# CREATOR: GIMP PNM Filter Version 1.1
1 256
255
255
255
255
128
0
0
0
128
0
128
128
0
0
0
128
128
0
128
0
128
128
192
192
192
192
220
192
166
202
240
255
255
255
240
240
240
221
221
221
203
203
203
187
187
187
178
178
178
168
168
168
157
157
157
147
147
147
137
137
137
127
127
127
117
117
117
106
106
106
96
96
96
86
86
86
76
76
76
61
61
61
49
49
49
36
36
36
24
24
24
12
12
12
0
0
0
134
134
255
113
113
241
93
93
228
72
72
214
63
63
200
55
55
186
46
46
172
38
38
158
29
29
144
20
20
131
12
12
117
3
3
103
3
3
91
3
3
79
3
3
68
3
3
56
255
145
145
242
123
123
230
101
101
217
79
79
205
70
70
193
61
61
181
53
53
169
44
44
157
35
35
144
26
26
132
18
18
120
9
9
108
8
8
94
7
7
79
7
7
65
6
6
147
142
185
132
126
172
117
109
159
102
93
146
95
86
133
88
78
123
82
73
115
77
67
107
72
61
100
67
55
92
61
50
84
56
44
76
51
38
68
46
32
60
40
27
52
35
21
44
200
150
137
187
130
115
175
110
94
164
95
77
154
79
61
143
64
44
137
60
42
132
55
38
125
50
33
120
48
29
111
44
26
103
39
24
94
35
21
83
30
18
72
25
14
61
20
10
121
107
34
109
94
29
96
82
25
84
69
20
77
62
17
70
55
14
63
47
12
56
40
9
93
120
53
80
103
42
66
86
31
53
69
20
49
60
16
45
52
12
43
44
10
43
38
8
136
145
44
118
128
37
101
111
30
83
94
23
70
79
17
56
65
11
42
50
6
28
36
0
57
134
64
48
118
54
38
101
43
29
85
33
22
71
25
15
58
17
7
45
8
0
32
0
143
87
56
126
75
45
110
64
35
93
52
24
85
44
16
72
36
12
64
32
8
56
24
4
127
96
54
115
85
46
102
75
39
90
64
31
82
57
25
75
51
20
68
44
15
61
38
10
141
86
56
126
75
46
110
65
36
95
54
26
88
51
25
73
40
18
57
29
10
42
18
3
172
199
199
138
173
173
104
148
148
71
122
122
37
97
97
3
71
71
4
56
56
4
41
41
217
209
200
202
194
184
188
178
167
173
163
151
158
147
134
148
136
123
137
125
112
126
114
101
116
104
91
105
93
80
94
82
69
84
71
58
73
60
47
62
50
36
52
39
25
41
28
14
231
232
207
219
217
180
208
201
152
196
186
125
184
171
98
173
155
70
161
140
43
150
129
39
139
119
37
127
109
33
117
99
29
105
89
25
90
76
21
75
62
18
60
49
14
45
35
10
128
99
127
113
73
112
97
47
97
82
21
82
75
2
74
68
2
67
52
3
50
35
4
33
247
178
102
229
152
75
212
127
48
194
101
21
179
87
16
161
73
12
142
59
9
124
45
5
255
0
0
194
3
3
161
2
2
255
227
11
209
185
8
169
150
6
103
190
255
2
130
232
4
4
209
0
255
0
7
180
7
3
132
3
255
114
230
255
17
205
203
6
156
246
164
73
221
123
16
177
95
4
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
132
18
18
113
13
13
94
7
7
107
8
8
120
9
9
193
62
62
181
54
54
168
45
45
157
36
36
144
27
27
62
50
36
57
45
31
52
39
25
46
34
20
41
28
14
255
251
240
160
160
164
128
128
128
255
0
0
0
255
0
255
255
0
0
0
255
255
0
255
0
255
255
255
255
255

Binary file not shown.

View File

@@ -0,0 +1,13 @@
To build this palette, I did the following:
* Created the .asn + .obj files by hand
* Referenced them in a .set file and asked WH40K_TD.exe to render the sprites
* Screenshotted the output and used GIMP to create a 1x255 image with them all
* Exported that image as an ASCII .ppm file
* Used the `generate_palette` script to output Go code \o/
Note that palette index 0 is ignored, and hardcoded to be transparent black.
This is because 0x00 seems to be used as a record separator in .obj files, so
can't be used as a palette index anyway.

Binary file not shown.

View File

@@ -18,7 +18,7 @@ module Obj
)
def self.parse(data)
hdr = new(*data[0..SIZE - 1].unpack("V*"))
hdr = new(*data[0..SIZE - 1].unpack("VVVVV"))
pp hdr
hdr.validate!(data.bytes.size)
hdr
@@ -28,6 +28,16 @@ module Obj
@num_sprites, @dir_offset, @dir_size, @data_offset, @data_size = *entries
end
def to_data
[
@num_sprites,
@dir_offset,
@dir_size,
@data_offset,
@data_size,
].pack("VVVVV")
end
def validate!(overall_size)
raise "Directory overlaps EOF" if overall_size < dir_size + dir_offset
raise "Data overlaps EOF" if overall_size < data_offset + data_size
@@ -62,6 +72,10 @@ module Obj
@sprite_size = sprite_size
end
def to_data
[rel_offset, sprite_size].pack("VV")
end
def sprite_range
rel_offset...(rel_offset+sprite_size)
end
@@ -91,8 +105,11 @@ module Obj
@entries = entries
end
# Convert the directory into an Array of bytes. Until we work out how to
# parse sprites, anyway...
def to_data
entries.map(&:to_data).join("")
end
# Convert the directory into an Array of sprites
def realize(rel_data)
entries.map { |entry| Sprite.parse(rel_data[entry.sprite_range]) }
end
@@ -125,6 +142,18 @@ module Obj
@unknown20 = *args
end
def to_data
[
@unknown0,
@width,
@height,
@unknown8,
@size,
@unknown16,
@unknown20
].pack("VvvVVVV")
end
def pixel_range
SIZE...(SIZE+size)
end
@@ -148,6 +177,15 @@ module Obj
@records = records
@raw = raw
end
# raw is optional, so don't use it here
def to_data
header.to_data + records.join("")
end
def total_size
SpriteHeader::SIZE + records.join("").size
end
end
class Parsed
@@ -161,6 +199,19 @@ module Obj
@directory = directory
@sprites = sprites
end
def to_data
dir_padding = "\x00"*(header.dir_offset - Header::SIZE)
data_padding = "" # for now, assume all is well
[
header.to_data,
dir_padding,
directory.to_data,
data_padding,
sprites.map { |sprite| sprite.to_data },
].join("")
end
end
def self.parse(data)
@@ -383,6 +434,70 @@ def sprite(filename, i)
end
end
# Build a test sprite to investigate the color palette
def build(filename)
sprite_records = [
# 63 in each sprite except the last, which has just 3
1.upto(63).map { |b| "\x01#{b.chr}\x00" },
64.upto(126).map { |b| "\x01#{b.chr}\x00" },
127.upto(189).map { |b| "\x01#{b.chr}\x00" },
190.upto(252).map { |b| "\x01#{b.chr}\x00" },
253.upto(255).map { |b| "\x01#{b.chr}\x00" },
]
sprites = sprite_records.map do |records|
data = records.join("")
header = Obj::SpriteHeader.new(
0, # Stolen from blank.obj
1, # width of 1
records.size, # height = number of records
0, # padding?
data.size,
0, # padding?
0, # padding?
)
Obj::Sprite.new(
header,
records,
header.to_data + data,
)
end
dir_block_offset = 32 # hardcoded
dir_block_size = 8 *sprites.size
data_block_offset = dir_block_offset + dir_block_size
data_block_size = sprites.inject(0) { |x, spr| x + spr.total_size }
header = Obj::Header.new(
sprites.size,
dir_block_offset,
dir_block_size,
data_block_offset,
data_block_size,
)
offset = 0
directory = Obj::SpriteDir.new(
sprites.map { |sprite|
puts "offset #{offset} => #{sprite.total_size}"
entry = Obj::DirEntry.new(offset, sprite.total_size)
offset += sprite.total_size
entry
}
)
pp directory
built = Obj::Parsed.new(
header,
directory,
sprites
)
File.open(filename, "w") { |f| f.write(built.to_data) }
end
case command = ARGV.shift
when "sprites" then
ARGV.each { |filename| sprites(filename) }
@@ -396,6 +511,8 @@ when "decompress" then
ARGV.each { |filename| decompress(filename) }
when "correlate" then
correlate(ARGV)
when "build" then
build(ARGV[0])
else
puts "Unrecognized command #{command}"
exit(1)