Subversion Repositories eduke32

Rev

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