Turns out the palette is actually identical to that in wh40k.pcx
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
/investigation
|
||||
/loader
|
||||
/orig
|
||||
/view-obj
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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},
|
||||
|
34
investigation/Palette/generate-palette
Executable file
34
investigation/Palette/generate-palette
Executable 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
|
12
investigation/Palette/palette.asn
Normal file
12
investigation/Palette/palette.asn
Normal file
@@ -0,0 +1,12 @@
|
||||
# jungle floor
|
||||
# jungtil.obj/.asn
|
||||
# /--> d:\warflics\missions\jungtil.flc
|
||||
#
|
||||
|
||||
0:DEF 2;
|
||||
|
||||
0:TYPE 2;
|
||||
|
||||
END OF FILE
|
||||
|
||||
|
BIN
investigation/Palette/palette.obj
Normal file
BIN
investigation/Palette/palette.obj
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
772
investigation/Palette/palette.ppm
Normal file
772
investigation/Palette/palette.ppm
Normal 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
|
BIN
investigation/Palette/palette.rgb.raw
Normal file
BIN
investigation/Palette/palette.rgb.raw
Normal file
Binary file not shown.
13
investigation/Palette/palette.txt
Normal file
13
investigation/Palette/palette.txt
Normal 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.
|
||||
|
||||
|
BIN
investigation/Palette/palette.xcf
Normal file
BIN
investigation/Palette/palette.xcf
Normal file
Binary file not shown.
@@ -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)
|
||||
|
Reference in New Issue
Block a user