Subversion Repositories eduke32

Rev

Rev 4311 | Rev 4331 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4236 helixhorne 1
 
2
local ffi = require("ffi")
3
local C = ffi.C
4
 
5
local bcarray = require("bcarray")
6
 
4312 helixhorne 7
local assert = assert
4236 helixhorne 8
local error = error
9
local type = type
10
 
11
local decl = assert(decl)  -- comes from above (defs.ilua or defs_m32.lua)
12
 
13
local ismapster32 = (C.LUNATIC_CLIENT == C.LUNATIC_CLIENT_MAPSTER32)
14
 
15
----------
16
 
17
decl[[
4308 helixhorne 18
int32_t getclosestcol_lim(int32_t r, int32_t g, int32_t b, int32_t lastokcol);
4236 helixhorne 19
char *palookup[256];  // MAXPALOOKUPS
20
uint8_t palette[768];
21
 
4301 helixhorne 22
const char *getblendtab(int32_t blend);
23
void setblendtab(int32_t blend, const char *tab);
24
 
4236 helixhorne 25
int32_t setpalookup(int32_t palnum, const uint8_t *shtab);
26
]]
27
 
28
----------
29
 
4242 helixhorne 30
 
4236 helixhorne 31
-- The API table
32
local engine = {}
33
 
4242 helixhorne 34
 
35
local shtab_t  -- forward-decl
36
 
4301 helixhorne 37
local function cast_u8ptr(sth)
38
    return ffi.cast("uint8_t *", sth)
4242 helixhorne 39
end
40
 
41
local shtab_methods = {
42
    -- Remap consecutive blocks of 16 color indices and return this new shade
43
    -- table.
44
    --
4256 helixhorne 45
    -- <idxs16>: table with idxs16[0] .. idxs16[15] >= 0 and <= 15
46
    --  (i.e. 0-based indices of such 16-tuples)
4242 helixhorne 47
    --
48
    -- For example, the table
49
    --  { [0]=0,1, 2,3, 5,4, 6,7, 8,13, 10,11, 12,9, 14,15 }
50
    -- TODO (...)
51
    remap16 = function(sht, idxs16)
52
        if (type(idxs16) ~= "table" or idxs16[0]==nil or #idxs16 ~= 15) then
53
            error("invalid argument #2: must be a [0]-table with 16 elements", 2)
54
        end
55
 
56
        for i=0,15 do
57
            if (not (idxs16[i] >= 0 and idxs16[i] <= 15)) then
58
                error("invalid reordering table: elements must be in [0 .. 15]", 2)
59
            end
60
        end
61
 
62
        local newsht = shtab_t()
63
        for sh=0,31 do
64
            for i=0,15 do
65
                ffi.copy(cast_u8ptr(newsht[sh]) + 16*i,
66
                         cast_u8ptr(sht[sh]) + 16*idxs16[i], 16)
67
            end
68
        end
69
        return newsht
70
    end,
71
}
72
 
73
local function shtab_mt__index(sht, idx)
74
    local method = shtab_methods[idx]
75
    if (method) then
76
        return method
77
    end
78
end
79
 
4301 helixhorne 80
local pal256_t = bcarray.new("uint8_t", 256, "color index 256-tuple")
4236 helixhorne 81
-- The shade table type, effectively a bound-checked uint8_t [32][256]:
4242 helixhorne 82
shtab_t = bcarray.new(pal256_t, 32, "shade table", nil, nil, { __index = shtab_mt__index })
4236 helixhorne 83
local SIZEOF_SHTAB = ffi.sizeof(shtab_t)
84
 
4301 helixhorne 85
local blendtab_t = bcarray.new(pal256_t, 256, "blending table")
86
local SIZEOF_BLENDTAB = ffi.sizeof(blendtab_t)
87
 
4236 helixhorne 88
local RESERVEDPALS = 8  -- KEEPINSYNC build.h: assure that ours is >= theirs
89
engine.RESERVEDPALS = RESERVEDPALS
90
 
4301 helixhorne 91
local MAXBLENDTABS = 256  -- KEEPINSYNC build.h
92
 
4236 helixhorne 93
local function check_palidx(i)
94
    if (type(i) ~= "number" or not (i >= 0 and i <= 255-RESERVEDPALS)) then
4262 helixhorne 95
        error("invalid argument #1: palette swap index must be in the range [0 .. "..255-RESERVEDPALS.."]", 3)
4236 helixhorne 96
    end
97
end
98
 
4301 helixhorne 99
local function check_blendidx(i)
100
    if (type(i) ~= "number" or not (i >= 0 and i <= MAXBLENDTABS-1)) then
101
        error("invalid argument #1: blending table index must be in the range [0 .. ".. MAXBLENDTABS-1 .."]", 3)
102
    end
103
end
104
 
4236 helixhorne 105
local function err_uncommon_shade_table(ret)
106
    if (ret == -1) then
107
        error("loaded engine shade tables don't have 32 gradients of shade", 3)
108
    end
109
end
110
 
111
local function palookup_isdefault(palnum)  -- KEEPINSYNC engine.c
112
    return (C.palookup[palnum] == nil or (palnum ~= 0 and C.palookup[palnum] == C.palookup[0]))
113
end
114
 
115
function engine.shadetab()
116
    return shtab_t()
117
end
118
 
4301 helixhorne 119
function engine.blendtab()
120
    return blendtab_t()
121
end
122
 
4236 helixhorne 123
function engine.getshadetab(palidx)
124
    check_palidx(palidx)
125
    if (palookup_isdefault(palidx)) then
126
        return nil
127
    end
128
 
129
    local ret = C.setpalookup(palidx, nil)
130
    err_uncommon_shade_table(ret)
131
 
132
    local sht = shtab_t()
133
    ffi.copy(sht, C.palookup[palidx], SIZEOF_SHTAB)
134
    return sht
135
end
136
 
4301 helixhorne 137
function engine.getblendtab(blendidx)
138
    check_blendidx(blendidx)
139
 
140
    local ptr = C.getblendtab(blendidx)
141
    if (ptr == nil) then
142
        return nil
143
    end
144
 
145
    local tab = blendtab_t()
146
    ffi.copy(tab, ptr, SIZEOF_BLENDTAB)
147
    return tab
148
end
149
 
150
 
151
local function check_first_time()
4236 helixhorne 152
    if (not ismapster32 and C.g_elFirstTime == 0) then
4301 helixhorne 153
        error("may be called only while LUNATIC_FIRST_TIME is true", 3)
4236 helixhorne 154
    end
4301 helixhorne 155
end
4236 helixhorne 156
 
4301 helixhorne 157
function engine.setshadetab(palidx, shtab)
158
    check_first_time()
4236 helixhorne 159
    check_palidx(palidx)
4301 helixhorne 160
 
161
    if (not ffi.istype(shtab_t, shtab)) then
4236 helixhorne 162
        error("invalid argument #2: must be a shade table obtained by shadetab()", 2)
163
    end
164
 
165
    if (not ismapster32 and not palookup_isdefault(palidx)) then
166
        error("attempt to override already defined shade table", 2)
167
    end
168
 
4301 helixhorne 169
    local ret = C.setpalookup(palidx, cast_u8ptr(shtab))
4236 helixhorne 170
    err_uncommon_shade_table(ret)
171
end
172
 
4301 helixhorne 173
function engine.setblendtab(blendidx, tab)
174
    check_first_time()
175
    check_blendidx(blendidx)
4236 helixhorne 176
 
4301 helixhorne 177
    if (not ffi.istype(blendtab_t, tab)) then
178
        error("invalid argument #2: must be a blending table obtained by blendtab()", 2)
179
    end
180
 
181
    if (not ismapster32 and C.getblendtab(blendidx) ~= nil) then
182
        error("attempt to override already defined blending table", 2)
183
    end
184
 
185
    C.setblendtab(blendidx, cast_u8ptr(tab))
186
end
187
 
188
 
4236 helixhorne 189
local function check_colcomp(a)
190
    if (type(a) ~= "number" or not (a >= 0 and a <= 63)) then
191
        error("color component must be in the range [0 .. 63]", 3)
192
    end
193
end
194
 
195
 
196
-- TODO: other base palettes?
197
function engine.getrgb(colidx)
198
    if (type(colidx) ~= "number" or not (colidx >= 0 and colidx <= 255)) then
199
        error("color index must be in the range [0 .. 255]", 2)
200
    end
201
 
4301 helixhorne 202
    -- NOTE: In the game, palette[255*{0..2}] is set to 0 in
203
    -- G_LoadExtraPalettes() via G_Startup(). However, that's after Lua state
204
    -- initialization (i.e. when LUNATIC_FIRST_TIME would be true), and in the
205
    -- editor, it's never changed from the purple color. Therefore, I think
206
    -- it's more useful to always return the fully black color here.
207
    if (colidx == 255) then
208
        return 0, 0, 0
209
    end
210
 
4236 helixhorne 211
    local rgbptr = C.palette + 3*colidx
212
    return rgbptr[0], rgbptr[1], rgbptr[2]
213
end
214
 
4308 helixhorne 215
function engine.nearcolor(r, g, b, lastokcol)
4236 helixhorne 216
    check_colcomp(r)
217
    check_colcomp(g)
218
    check_colcomp(b)
4308 helixhorne 219
 
220
    if (lastokcol == nil) then
221
        lastokcol = 255
222
    elseif (type(lastokcol)~="number" or not (lastokcol >= 0 and lastokcol <= 255)) then
223
        error("invalid argument #4 <lastokcol>: must be in the range [0 .. 255]", 2)
224
    end
225
 
226
    return C.getclosestcol_lim(r, g, b, lastokcol)
4236 helixhorne 227
end
228
 
229
 
4311 helixhorne 230
---------- Mapster32-only functions ----------
231
 
232
if (ismapster32) then
233
    local io = require("io")
4312 helixhorne 234
    local math = require("math")
235
    local string = require("string")
4311 helixhorne 236
 
237
    ffi.cdef[[size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, void * restrict stream);]]
238
 
4312 helixhorne 239
    local function validate_more_blendtabs(moreblends)
240
        if (moreblends == nil) then
241
            return nil, nil
242
        end
243
 
244
        -- Additional blending tables: validate <moreblends> table.
245
        if (type(moreblends) ~= "table") then
246
            error("invalid argument #4: must be a table", 3)
247
        end
248
 
249
        local haveblend = { [0]=true }
250
        local blendnumtab, blendptrtab = {}, {}
251
 
252
        for i=1,#moreblends do
253
            local tmp = moreblends[i]
254
            local blendspec = (type(tmp) == "number") and { tmp, tmp } or tmp
255
 
256
            if (not (type(blendspec) == "table" and #blendspec == 2)) then
257
                error("invalid argument #4: must contain numbers or 2-tables", 3)
258
            end
259
 
260
            local blend1, blend2 = math.floor(blendspec[1]), math.floor(blendspec[2])
261
 
262
            if (not (type(blend1)=="number" and blend1 >= 1 and blend1 <= 255 and
263
                     type(blend2)=="number" and blend2 >= 1 and blend2 <= 255)) then
264
                error("invalid argument #4: blending table numbers must be in [1 .. 255]", 3)
265
            end
266
 
267
            for bi=blend1,blend2 do
268
                if (haveblend[bi]) then
269
                    error("invalid argument #4: duplicate blending table number "..bi, 3)
270
                end
271
                haveblend[bi] = true
272
 
273
                local ptr = C.getblendtab(bi)
274
                if (ptr == nil) then
275
                    error("invalid argument #4: blending table for number "..bi.." is void", 3)
276
                end
277
 
278
                blendnumtab[#blendnumtab+1] = bi
279
                blendptrtab[#blendptrtab+1] = ptr
280
            end
281
        end
282
 
283
        assert(#blendnumtab <= 255)
284
        return blendnumtab, blendptrtab
285
    end
286
 
287
    -- [ok, errmsg] = engine.savePaletteDat(filename [, palnum [, blendnum [, moreblends]]])
288
    function engine.savePaletteDat(filename, palnum, blendnum, moreblends)
4311 helixhorne 289
        local sht = engine.getshadetab(palnum or 0)
290
        local tab = engine.getblendtab(blendnum or 0)
291
 
292
        if (sht == nil) then
293
            return nil, "no shade table with number "..palnum
294
        elseif (tab == nil) then
295
            return nil, "no blending table with number "..blendnum
296
        end
297
 
4312 helixhorne 298
        local blendnumtab, blendptrtab = validate_more_blendtabs(moreblends)
299
 
4311 helixhorne 300
        local f, errmsg = io.open(filename, "w+")
301
        if (f == nil) then
302
            return nil, errmsg
303
        end
304
 
4312 helixhorne 305
        local n1 = C.fwrite(C.palette, 3, 256, f)
4311 helixhorne 306
        f:write("\032\000")  -- int16_t numshades
4312 helixhorne 307
        local n3 = C.fwrite(sht, 256, 32, f)
308
        local n4 = C.fwrite(tab, 256, 256, f)
4311 helixhorne 309
 
310
        if (n1 ~= 256 or n3 ~= 32 or n4 ~= 256) then
4312 helixhorne 311
            return nil, "failed writing classic PALETTE.DAT data"
4311 helixhorne 312
        end
313
 
4312 helixhorne 314
        if (blendnumtab ~= nil) then
315
            f:write("MoreBlendTab")
316
            f:write(string.char(#blendnumtab))
317
 
318
            for i=1,#blendnumtab do
319
                f:write(string.char(blendnumtab[i]))
320
                if (C.fwrite(blendptrtab[i], 256, 256, f) ~= 256) then
321
                    return nil, "failed writing additional blending table"
322
                end
323
            end
324
        end
325
 
326
        f:close()
327
 
4311 helixhorne 328
        return true
329
    end
330
end
331
 
332
 
4236 helixhorne 333
-- Done!
334
return engine