Subversion Repositories eduke32

Rev

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