From 53c9169330d96ce99b1b8eebfba5b0ced4b6fd7c Mon Sep 17 00:00:00 2001 From: KubaPro010 Date: Tue, 20 Jan 2026 19:50:04 +0100 Subject: [PATCH] massive lua api refactor --- .vscode/settings.json | 5 +- plugin.lua | 196 +++++++------ scripts/0-oda.lua | 21 +- scripts/0-rds2_oda.lua | 14 +- scripts/1-af.lua | 18 +- scripts/1-ert.lua | 56 ++-- scripts/1-rft.lua | 12 +- scripts/1-rtp.lua | 58 ++-- scripts/99-data.lua | 154 +++++----- src/lua_api.c | 425 +++++++++++++++++++++++++++ src/lua_api.h | 101 +++++++ src/lua_rds.c | 651 ++++++++++------------------------------- src/lua_rds.h | 10 +- src/rds.c | 8 +- src/rds95.c | 23 +- 15 files changed, 976 insertions(+), 776 deletions(-) create mode 100644 src/lua_api.c create mode 100644 src/lua_api.h diff --git a/.vscode/settings.json b/.vscode/settings.json index e0a4449..bff6bd0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,5 +30,8 @@ "ffi": "disable", "package": "disable" }, - "C_Cpp.dimInactiveRegions": false + "C_Cpp.dimInactiveRegions": false, + "Lua.diagnostics.disable": [ + "duplicate-set-field" + ] } \ No newline at end of file diff --git a/plugin.lua b/plugin.lua index 310abb0..72313c3 100644 --- a/plugin.lua +++ b/plugin.lua @@ -3,48 +3,62 @@ -- Global Variables ---@type string core_version = "" ----@type integer -eon_count = 0 ----@type integer -max_programs = 0 ----@type integer -user_data_len = 0 --#region Functions implemented or used in C ----Executes a CRC-16 CCIIT +dp = {} + +---@type integer +dp.max_programs = 0 + +---Executes a CRC-16 CCIIT on given data ---@param data string ---@return integer -function crc16(data) end +function dp.crc16(data) end ---Starts the initialization sequence, also calls the on_init function ---@return nil -function set_rds_program_defaults() end +function dp.set_rds_program_defaults() end ---Saves, loads and resets the state of the data, you might as well restart the whole program ---@return nil -function reset_rds() end +function dp.reset_rds() end ---Forces encoder and modulator data to be saved to disc ---@return nil -function force_save() end +function dp.force_save() end + +---@param program_idx integer 0 to (max_programs - 1) +function dp.set_rds_program(program_idx) end +---@return integer +function dp.get_rds_program() end ---This function is called by the C core after we reset data, or have no data in general ---It should be defined by the user in the script. ---@return nil function on_init() end +on_inits = {} + ---This function is called by the C core after we initialize the encoder (always, every start) ---It should be defined by the user in the script. ---@return nil function on_start() end +on_starts = {} + ---This function is called every time when the state resets, register your odas here ---It should be defined by the user in the script. ---@return nil function on_state() end +on_states = {} + ---This function is called every second ---It should be defined by the user in the script. ---@return nil function tick() end +ticks = {} + +-- Every function with with the s suffixed table means that other versions of that function can be in that list-table, and each one will be called + ---This function is called in order to handle UDP data ---It should be defined by the user in the script. ---@param data string @@ -70,72 +84,77 @@ function group(group) end ---@return integer d function rds2_group(stream) end +rds = {} + +---@type integer +rds.eon_count = 0 + ---@param pi integer -function set_rds_pi(pi) end +function rds.set_pi(pi) end ---@return integer -function get_rds_pi() end +function rds.get_pi() end ---@param pty integer -function set_rds_pty(pty) end +function rds.set_pty(pty) end ---@return integer -function get_rds_pty() end +function rds.get_pty() end ---@param ecc integer -function set_rds_ecc(ecc) end +function rds.set_ecc(ecc) end ---@return integer -function get_rds_ecc() end +function rds.get_ecc() end ---@param slc_data integer -function set_rds_slc_data(slc_data) end +function rds.set_slc_data(slc_data) end ---@return integer -function get_rds_slc_data() end +function rds.get_slc_data() end ---@param ct boolean -function set_rds_ct(ct) end +function rds.set_ct(ct) end ---@return boolean -function get_rds_ct() end +function rds.get_ct() end ---@param dpty boolean -function set_rds_dpty(dpty) end +function rds.set_dpty(dpty) end ---@return boolean -function get_rds_dpty() end +function rds.get_dpty() end ---@param tp boolean -function set_rds_tp(tp) end +function rds.set_tp(tp) end ---@return boolean -function get_rds_tp() end +function rds.get_tp() end ---@param ta boolean -function set_rds_ta(ta) end +function rds.set_ta(ta) end ---@return boolean -function get_rds_ta() end +function rds.get_ta() end -- Feature Flags ---@param enabled boolean -function set_rds_rt1_enabled(enabled) end +function rds.set_rt1_enabled(enabled) end ---@return boolean -function get_rds_rt1_enabled() end +function rds.get_rt1_enabled() end ---@param enabled boolean -function set_rds_rt2_enabled(enabled) end +function rds.set_rt2_enabled(enabled) end ---@return boolean -function get_rds_rt2_enabled() end +function rds.get_rt2_enabled() end ---@param enabled boolean -function set_rds_ptyn_enabled(enabled) end +function rds.set_ptyn_enabled(enabled) end ---@return boolean -function get_rds_ptyn_enabled() end +function rds.get_ptyn_enabled() end ---@param rt_type integer -function set_rds_rt_type(rt_type) end +function rds.set_rt_type(rt_type) end ---@return integer -function get_rds_rt_type() end +function rds.get_rt_type() end -- Modulation & Generation ---@param mode integer -function set_rds2_mode(mode) end +function rds.set_rds2_mode(mode) end ---@return integer -function get_rds2_mode() end +function rds.get_rds2_mode() end ---@param streams integer function set_rds_streams(streams) end @@ -152,72 +171,67 @@ function get_rds_level() end -- Program & Linking ---@param linkage boolean -function set_rds_link(linkage) end +function rds.set_link(linkage) end ---@return boolean -function get_rds_link() end - ----@param program_idx integer 0 to (max_programs - 1) -function set_rds_program(program_idx) end ----@return integer -function get_rds_program() end +function rds.get_link() end -- Timeouts and Periods ---@param period integer in seconds -function set_rds_rt_switching_period(period) end +function rds.set_rt_switching_period(period) end ---@return integer -function get_rds_rt_switching_period() end +function rds.get_rt_switching_period() end ---For a RT1, this sets the timeout period before setting RT1 into "Default RT" (not literally) ---@param timeout integer in seconds -function set_rds_rt_text_timeout(timeout) end +function rds.set_rt_text_timeout(timeout) end ---@return integer -function get_rds_rt_text_timeout() end +function rds.get_rt_text_timeout() end -- String Setters (Charset converted) ---@param ptyn string Program Type Name (max 8 chars) -function set_rds_ptyn(ptyn) end +function rds.set_ptyn(ptyn) end ---@param ps string Program Service (8 chars) -function set_rds_ps(ps) end +function rds.set_ps(ps) end ---@param tps string Traffic PS -function set_rds_tps(tps) end +function rds.set_tps(tps) end ---@param rt1 string Radio Text 1 (max 64 chars) -function set_rds_rt1(rt1) end +function rds.set_rt1(rt1) end ---@param rt2 string Radio Text 2 (max 64 chars) -function set_rds_rt2(rt2) end +function rds.set_rt2(rt2) end ---@param rt string Default radio text - max 64 characters -function set_rds_default_rt(rt) end +function rds.set_default_rt(rt) end ---@param lps string -function set_rds_lps(lps) end +function rds.set_lps(lps) end ---@return string -function get_rds_lps() end +function rds.get_lps() end ---@param grpseq string -function set_rds_grpseq(grpseq) end +function rds.set_grpseq(grpseq) end ---@return string -function get_rds_grpseq() end +function rds.get_grpseq() end ---@param grpseq2 string -function set_rds_grpseq2(grpseq2) end +function rds.set_grpseq2(grpseq2) end ---@return string -function get_rds_grpseq2() end +function rds.get_grpseq2() end ---Puts in a RDS1 group in the buffer, note that block A is filled in always ---@param b integer ---@param c integer ---@param d integer -function put_rds_custom_group(b, c, d) end +function rds.put_custom_group(b, c, d) end ---Puts in a RDS2 group in the buffer ---@param a integer ---@param b integer ---@param c integer ---@param d integer -function put_rds2_custom_group(a, b, c, d) end +function rds.put_rds2_custom_group(a, b, c, d) end ---Sets the AFs included in group 0 ---@param afs table -function set_rds_af_group0(afs) end +function rds.set_af(afs) end ---Sets data about the EON ---@param eon integer Index of the EON we are setting @@ -229,7 +243,7 @@ function set_rds_af_group0(afs) end ---@param ps string ---@param afs table ---@param data integer -function set_rds_eon(eon, enabled, pi, tp, ta, pty, ps, afs, data) end +function rds.set_eon(eon, enabled, pi, tp, ta, pty, ps, afs, data) end ---Gets the same data set_rds_eon sets, yes this returns 8 arguments ---@param eon integer @@ -241,37 +255,45 @@ function set_rds_eon(eon, enabled, pi, tp, ta, pty, ps, afs, data) end ---@return string ps ---@return table _ this is empty, getting afs is not supported yet ---@return integer data -function get_rds_eon(eon) end +function rds.get_eon(eon) end ---Sets the X/Y of the UDG ---@param xy boolean ---@param groups table Table of tables, this should be up to 8 tables containing 3 integers -function set_rds_udg(xy, groups) end +function rds.set_udg(xy, groups) end ---Sets the X/Y of the UDG for RDS2 ---@param xy boolean ---@param groups table Table of tables, this should be up to 8 tables containing 4 integers -function set_rds_udg2(xy, groups) end +function rds.set_udg2(xy, groups) end + +userdata = {} + +---@type integer +userdata.len = 0 ---Data is allocated in each program's data for lua data (per program, diffrent program, diffrent data), note that this overwrites existing data over the whole userdata string ---@param data string -function set_userdata(data) end +function userdata.set(data) end ---Writes to the userdata at the offset, size does not have to match the length of the string, if the string is less than size then the rest of the string will be padded with zeroes until it is size ---@param offset integer ---@param size integer ---@param data string -function set_userdata_offset(offset, size, data) end +function userdata.set_offset(offset, size, data) end ---Returns all of the data saved as user data ---@return string -function get_userdata() end +function userdata.get() end ---Gets data from userdata but at the specified offset ---@param offset integer ---@param size integer ---@return string -function get_userdata_offset(offset, size) end +function userdata.get_offset(offset, size) end --#endregion +ext = {} +rds.ext = {} + -- RT Plus Tags ---Sets RT+ tags: type1, start1, len1, type2, start2, len2 ---@param ertp boolean @@ -281,29 +303,29 @@ function get_userdata_offset(offset, size) end ---@param t2 integer ---@param s2 integer ---@param l2 integer -function set_rds_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2) end +function rds.ext.set_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2) end ---Gets RT+ tags: type1, start1, len1, type2, start2, len2 ---@param ertp boolean ---@return integer type1, integer start1, integer len1, integer type2, integer start2, integer len2 -function get_rds_rtplus_tags(ertp) end +function rds.ext.get_rtplus_tags(ertp) end ---Toggles RTP or ERTP's toggle switch ---@param ertp boolean -function toggle_rds_rtp(ertp) end +function rds.ext.toggle_rtp(ertp) end ---Sets the metadata of RTP or ERTP ---@param ertp boolean ---@param running boolean -function set_rds_rtp_meta(ertp, running) end +function rds.ext.set_rtp_meta(ertp, running) end ---Gets the metadata of RTP and ERTP ---@param ertp boolean ---@return boolean running -function get_rds_rtp_meta(ertp) end +function rds.ext.get_rtp_meta(ertp) end ---Sets the AFs included in the ODA ---@param afs table -function set_rds_af_oda(afs) end +function rds.ext.set_af_oda(afs) end ---Registers an ODA to be used in the O of the group sequence. ODAs are stored as state data, thus running reset_rds will clear it ---Groups 14, 15, 2, 0 cannot be registered either version, groups 10, 4, 1 can be only registered as B, any other is free to take @@ -313,16 +335,16 @@ function set_rds_af_oda(afs) end ---@param aid integer ---@param data integer ---@return integer oda_id -function register_oda(group, group_version, aid, data) end +function ext.register_oda(group, group_version, aid, data) end ---Unregisters an ODA, this stops the handler or AID being called/sent ---@param oda_id integer -function unregister_oda(oda_id) end +function ext.unregister_oda(oda_id) end ---Sets the data for a existing ODA group ---@param oda_id integer ---@param data integer -function set_oda_id_data(oda_id, data) end +function ext.set_oda_id_data(oda_id, data) end ---The callback function for an ODA handler ---@alias ODAHandler fun(): (boolean, integer, integer, integer) @@ -334,7 +356,7 @@ function set_oda_id_data(oda_id, data) end ---You are asked to set groups B last 5 bits, leave rest 0 ---@param oda_id integer The ID returned by register_oda ---@param fun ODAHandler -function set_oda_handler(oda_id, fun) end +function ext.set_oda_handler(oda_id, fun) end ---The callback function for an ODA handler ---@alias RDS2_ODAHandler fun(integer): (boolean, integer, integer, integer, integer) @@ -342,23 +364,23 @@ function set_oda_handler(oda_id, fun) end ---You are asked to not fill in the channel id in block A, however you are asked to fill in the function number (if you do not know what is that, just OR block A with (1 << 14)) ---@param oda_id integer ---@param func RDS2_ODAHandler -function set_oda_handler_rds2(oda_id, func) end +function ext.set_oda_handler_rds2(oda_id, func) end ---@param oda_id integer ---@param data integer -function set_oda_id_data_rds2(oda_id, data) end +function ext.set_oda_id_data_rds2(oda_id, data) end ---@param aid integer ---@param data integer ---@param file_related boolean ---@return integer oda_id -function register_oda_rds2(aid, data, file_related) end +function ext.register_oda_rds2(aid, data, file_related) end ---Unregisters an RDS 2 ODA, this stops the handler or AID being called/sent ---@param oda_id integer -function unregister_oda_rds2(oda_id) end +function ext.unregister_oda_rds2(oda_id) end ---@param ert string -function set_rds_ert(ert) end +function rds.ext.set_ert(ert) end ---@return string -function get_rds_ert() end \ No newline at end of file +function rds.ext.get_ert() end \ No newline at end of file diff --git a/scripts/0-oda.lua b/scripts/0-oda.lua index 5c32ae7..e0dc2be 100644 --- a/scripts/0-oda.lua +++ b/scripts/0-oda.lua @@ -17,7 +17,7 @@ local _RDS_ODA_pointer = 1 ---@param aid integer ---@param data integer ---@return integer oda_id -function register_oda(group, group_version, aid, data) +function ext.register_oda(group, group_version, aid, data) if group == 14 or group == 15 or group == 2 or group == 0 then error("Group is incorrect", 2) end if (group == 10 or group == 4 or group == 1) and group_version then error("Group is incorrect", 2) end local oda = _ODA.new(group, group_version, aid, data, false) @@ -33,7 +33,7 @@ end ---Unregisters an ODA, this stops the handler or AID being called/sent ---@param oda_id integer -function unregister_oda(oda_id) +function ext.unregister_oda(oda_id) if oda_id < 1 or oda_id > #_RDS_ODAs or _RDS_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end _RDS_ODAs[oda_id] = false @@ -45,7 +45,7 @@ end ---Sets the id_data for a existing ODA group ---@param oda_id integer ---@param data integer -function set_oda_id_data(oda_id, data) +function ext.set_oda_id_data(oda_id, data) if oda_id < 1 or oda_id > #_RDS_ODAs or _RDS_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end _RDS_ODAs[oda_id].data = data end @@ -57,7 +57,7 @@ end ---You are asked to set groups B last 5 bits, leave rest 0 ---@param oda_id integer The ID returned by register_oda ---@param fun ODAHandler -function set_oda_handler(oda_id, fun) +function ext.set_oda_handler(oda_id, fun) if oda_id < 1 or oda_id > #_RDS_ODAs or _RDS_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end if _RDS_ODAs[oda_id].group == 3 then error("3A ODAs cannot have handlers.", 2) end _RDS_ODAs[oda_id].handler = fun @@ -113,18 +113,13 @@ function group(group_type) if #_RDS_ODAs == 0 then return false, 0, 0, 0 end if _RDS_ODA_pointer > #_RDS_ODAs or _RDS_ODA_pointer < 1 then _RDS_ODA_pointer = 1 end - if group_type == "O" then - return get_aid() - elseif group_type == "K" then - return get_data() - end + if group_type == "O" then return get_aid() + elseif group_type == "K" then return get_data() end end return false, 0, 0, 0 end -local _old_on_state_oda = on_state -function on_state() +table.insert(on_states, function () _RDS_ODAs = {} _RDS_ODA_pointer = 1 - if type(_old_on_state_oda) == "function" then _old_on_state_oda() end -end \ No newline at end of file +end) \ No newline at end of file diff --git a/scripts/0-rds2_oda.lua b/scripts/0-rds2_oda.lua index 272a89d..635dafb 100644 --- a/scripts/0-rds2_oda.lua +++ b/scripts/0-rds2_oda.lua @@ -14,7 +14,7 @@ _RDS2_ODA_pointer = 1 ---@param data integer ---@param file_related boolean ---@return integer oda_id -function register_oda_rds2(aid, data, file_related) +function ext.register_oda_rds2(aid, data, file_related) local oda = _RDS2_ODA.new(aid, data, false, file_related) for i = 1, #_RDS2_ODAs do if _RDS2_ODAs[i] == false then @@ -28,7 +28,7 @@ end ---Unregisters an RDS 2 ODA, this stops the handler or AID being called/sent ---@param oda_id integer -function unregister_oda_rds2(oda_id) +function ext.unregister_oda_rds2(oda_id) if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end _RDS2_ODAs[oda_id] = false @@ -39,14 +39,14 @@ end ---@param oda_id integer ---@param data integer -function set_oda_id_data_rds2(oda_id, data) +function ext.set_oda_id_data_rds2(oda_id, data) if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end _RDS2_ODAs[oda_id].data = data end ---@param oda_id integer ---@param func RDS2_ODAHandler -function set_oda_handler_rds2(oda_id, func) +function ext.set_oda_handler_rds2(oda_id, func) if oda_id < 1 or oda_id > #_RDS2_ODAs or _RDS2_ODAs[oda_id] == false then error("Invalid ODA ID: " .. tostring(oda_id), 2) end _RDS2_ODAs[oda_id].handler = func end @@ -125,10 +125,8 @@ function rds2_group(stream) end end -local _old_on_state_oda_rds2 = on_state -function on_state() +table.insert(on_states, function () _RDS2_ODAs = {} _RDS2_ODA_aid = 0 _RDS2_ODA_pointer = 1 - if type(_old_on_state_oda_rds2) == "function" then _old_on_state_oda_rds2() end -end \ No newline at end of file +end) \ No newline at end of file diff --git a/scripts/1-af.lua b/scripts/1-af.lua index 4d2eef2..3cf4a3f 100644 --- a/scripts/1-af.lua +++ b/scripts/1-af.lua @@ -55,8 +55,8 @@ end local function init_af_oda() if _Af_Oda_id == nil then - _Af_Oda_id = register_oda(7, false, 0x6365, 0) - set_oda_handler(_Af_Oda_id, function() + _Af_Oda_id = ext.register_oda(7, false, 0x6365, 0) + ext.set_oda_handler(_Af_Oda_id, function() local b, c, d = get_next_af_oda_group() return true, b, c, d end) @@ -69,7 +69,7 @@ local function save_af_to_userdata(afs) local payload = string.pack("B", count) for i = 1, count do payload = payload .. string.pack("f", afs[i]) end - set_userdata_offset(USERDATA_ODA_OFFSET, #payload, payload) + userdata.set_offset(USERDATA_ODA_OFFSET, #payload, payload) end local function _process_af_list(afs) @@ -86,13 +86,13 @@ local function _process_af_list(afs) end local function load_af_from_userdata() - local header = get_userdata_offset(USERDATA_ODA_OFFSET, 1) + local header = userdata.get_offset(USERDATA_ODA_OFFSET, 1) if header == "" or header == nil then return end local count = string.unpack("B", header) if count == 0 or count > 25 then return end - local data = get_userdata_offset(USERDATA_ODA_OFFSET + 1, count * 4) + local data = userdata.get_offset(USERDATA_ODA_OFFSET + 1, count * 4) if #data < (count * 4) then return end local afs = {} @@ -106,14 +106,12 @@ end ---Sets the AFs included in the ODA and saves them ---@param afs table List of numbers (e.g., {98.1, 102.5}) -function set_rds_af_oda(afs) +function rds.ext.set_af_oda(afs) _process_af_list(afs) save_af_to_userdata(afs) end -local _old_on_state_af = on_state -function on_state() +table.insert(on_states, function () load_af_from_userdata() if _Af_Oda_len ~= 0 then init_af_oda() end - if type(_old_on_state_af) == "function" then _old_on_state_af() end -end \ No newline at end of file +end) \ No newline at end of file diff --git a/scripts/1-ert.lua b/scripts/1-ert.lua index 233208c..2086d29 100644 --- a/scripts/1-ert.lua +++ b/scripts/1-ert.lua @@ -6,25 +6,25 @@ local USERDATA_ERT_OFFSET = 0 local function init_ert() if _Ert_oda_id == nil then - _Ert_oda_id = register_oda(13, false, 0x6552, 1) - set_oda_handler(_Ert_oda_id, function () - if string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+258, 1)) == 1 then - local new_data = get_userdata_offset(USERDATA_ERT_OFFSET, 128) - local new_segments = string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+128, 1)) - set_userdata_offset(USERDATA_ERT_OFFSET+129, 128, new_data) - set_userdata_offset(USERDATA_ERT_OFFSET+257, 1, string.char(new_segments)) - set_userdata_offset(USERDATA_ERT_OFFSET+258, 1, string.char(0)) + _Ert_oda_id = ext.register_oda(13, false, 0x6552, 1) + ext.set_oda_handler(_Ert_oda_id, function () + if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+258, 1)) == 1 then + local new_data = userdata.get_offset(USERDATA_ERT_OFFSET, 128) + local new_segments = string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+128, 1)) + userdata.set_offset(USERDATA_ERT_OFFSET+129, 128, new_data) + userdata.set_offset(USERDATA_ERT_OFFSET+257, 1, string.char(new_segments)) + userdata.set_offset(USERDATA_ERT_OFFSET+258, 1, string.char(0)) _Ert_state = 0 end - local segments = string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+257, 1)) + local segments = string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1)) if segments == 0 then return false, 0, 0, 0 end if _Ert_state >= segments then _Ert_state = 0 end local b = _Ert_state & 31 - local chunk = get_userdata_offset(USERDATA_ERT_OFFSET + 129 + _Ert_state * 4, 4) + local chunk = userdata.get_offset(USERDATA_ERT_OFFSET + 129 + _Ert_state * 4, 4) local c = (string.byte(chunk, 1) << 8) | string.byte(chunk, 2) local d = (string.byte(chunk, 3) << 8) | string.byte(chunk, 4) @@ -36,16 +36,16 @@ end function unregister_ert() if _Ert_oda_id ~= nil then - unregister_oda(_Ert_oda_id) + ext.unregister_oda(_Ert_oda_id) _Ert_oda_id = nil end end -function set_rds_ert(ert) +function rds.ext.set_ert(ert) if #ert == 0 then - set_userdata_offset(USERDATA_ERT_OFFSET, 128, "") - set_userdata_offset(USERDATA_ERT_OFFSET+128, 1, string.char(0)) - set_userdata_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1)) + userdata.set_offset(USERDATA_ERT_OFFSET, 128, "") + userdata.set_offset(USERDATA_ERT_OFFSET+128, 1, string.char(0)) + userdata.set_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1)) return end @@ -55,33 +55,31 @@ function set_rds_ert(ert) local padding = (4 - (#data % 4)) % 4 data = data .. string.rep("\0", padding) - set_userdata_offset(USERDATA_ERT_OFFSET, 128, data) + userdata.set_offset(USERDATA_ERT_OFFSET, 128, data) local segments = #data // 4 if segments > 32 then segments = 32 end - set_userdata_offset(USERDATA_ERT_OFFSET+128, 1, string.char(segments)) + userdata.set_offset(USERDATA_ERT_OFFSET+128, 1, string.char(segments)) - if string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+257, 1)) == 0 then + if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1)) == 0 then init_ert() - set_userdata_offset(USERDATA_ERT_OFFSET+129, 128, data) - set_userdata_offset(USERDATA_ERT_OFFSET+257, 1, string.char(segments)) + userdata.set_offset(USERDATA_ERT_OFFSET+129, 128, data) + userdata.set_offset(USERDATA_ERT_OFFSET+257, 1, string.char(segments)) _Ert_state = 0 - else set_userdata_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1)) end + else userdata.set_offset(USERDATA_ERT_OFFSET+258, 1, string.char(1)) end if _Ert_oda_id == nil then init_ert() end end -function get_rds_ert() - local segments = string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+128, 1)) +function rds.ext.get_ert() + local segments = string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+128, 1)) if segments == 0 then return "" end - local data = get_userdata_offset(USERDATA_ERT_OFFSET, 128) + local data = userdata.get_offset(USERDATA_ERT_OFFSET, 128) return data:match("^(.-)[\r%z]*") or "" end -local _old_on_state_ert = on_state -function on_state() - if string.byte(get_userdata_offset(USERDATA_ERT_OFFSET+257, 1)) ~= 0 then init_ert() end - if type(_old_on_state_ert) == "function" then _old_on_state_ert() end -end \ No newline at end of file +table.insert(on_states, function () + if string.byte(userdata.get_offset(USERDATA_ERT_OFFSET+257, 1)) ~= 0 then init_ert() end +end) \ No newline at end of file diff --git a/scripts/1-rft.lua b/scripts/1-rft.lua index d4c39ef..9ca16ea 100644 --- a/scripts/1-rft.lua +++ b/scripts/1-rft.lua @@ -43,7 +43,7 @@ end function RftInstance:stop() if self.oda_id ~= nil and self.aid ~= 0 then - unregister_oda_rds2(self.oda_id) + ext.unregister_oda_rds2(self.oda_id) self.oda_id = nil end @@ -59,9 +59,9 @@ end ---@private function RftInstance:start() if self.oda_id == nil and self.aid ~= 0 then - self.oda_id = register_oda_rds2(self.aid, 0, true) + self.oda_id = ext.register_oda_rds2(self.aid, 0, true) - set_oda_handler_rds2(self.oda_id, function(stream) + ext.set_oda_handler_rds2(self.oda_id, function(stream) if #self.file_data == 0 or self.paused then return false, 0, 0, 0, 0 end @@ -138,7 +138,7 @@ function RftInstance:sendFile(aid, path, id, crc, once) local f_size = #self.file_data if crc == 0 then self.crc_mode = 0 - self.crc_full_file = crc16(self.file_data) + self.crc_full_file = dp.crc16(self.file_data) elseif crc == true or crc == 7 then if f_size <= 40960 then self.crc_mode = 1 elseif f_size > 40960 and f_size <= 81920 then self.crc_mode = 2 @@ -153,7 +153,7 @@ function RftInstance:sendFile(aid, path, id, crc, once) local chunk_size = 5 * multiplier for i = 1, f_size, chunk_size do local chunk = string.sub(self.file_data, i, i + chunk_size - 1) - local v = crc16(chunk) + local v = dp.crc16(chunk) self.crc_data = self.crc_data .. string.char(v >> 8, v & 0xff) end end @@ -161,7 +161,7 @@ function RftInstance:sendFile(aid, path, id, crc, once) if f_size > (0x3ffff * 5) then error("File too large") end if self.oda_id == nil then self:start() end - set_oda_id_data_rds2(self.oda_id, f_size | (id & 63) << 18 | (self.version & 7) << 24 | (self.crc_enabled and 1 or 0) << 27) + ext.set_oda_id_data_rds2(self.oda_id, f_size | (id & 63) << 18 | (self.version & 7) << 24 | (self.crc_enabled and 1 or 0) << 27) self.last_id = id self.paused = false return interrupted diff --git a/scripts/1-rtp.lua b/scripts/1-rtp.lua index 6f6e781..41ec878 100644 --- a/scripts/1-rtp.lua +++ b/scripts/1-rtp.lua @@ -8,11 +8,11 @@ local USERDATA_RTP_OFFSET = 259 local function init_rtp() if _Rtp_oda_id == nil then - _Rtp_oda_id = register_oda(11, false, 0x4BD7, 0) - set_oda_handler(_Rtp_oda_id, function () - local b = (_Rtp_toggle and 1 or 0) << 4 | string.byte(get_userdata_offset(USERDATA_RTP_OFFSET, 1)) << 3 - local data_0 = get_userdata_offset(USERDATA_RTP_OFFSET+1, 3) - local data_1 = get_userdata_offset(USERDATA_RTP_OFFSET+4, 3) + _Rtp_oda_id = ext.register_oda(11, false, 0x4BD7, 0) + ext.set_oda_handler(_Rtp_oda_id, function () + local b = (_Rtp_toggle and 1 or 0) << 4 | string.byte(userdata.get_offset(USERDATA_RTP_OFFSET, 1)) << 3 + local data_0 = userdata.get_offset(USERDATA_RTP_OFFSET+1, 3) + local data_1 = userdata.get_offset(USERDATA_RTP_OFFSET+4, 3) b = b | (string.byte(data_0, 1) & 0xf8) >> 3 local c = (string.byte(data_0, 1) & 0x7) << 13 @@ -31,11 +31,11 @@ end local function init_ertp() if _Ertp_oda_id == nil then - _Ertp_oda_id = register_oda(12, false, 0x4BD8, 0) - set_oda_handler(_Ertp_oda_id, function () - local b = (_Ertp_toggle and 1 or 0) << 4 | string.byte(get_userdata_offset(USERDATA_RTP_OFFSET+7, 1)) << 3 - local data_0 = get_userdata_offset(USERDATA_RTP_OFFSET+8, 3) - local data_1 = get_userdata_offset(USERDATA_RTP_OFFSET+11, 3) + _Ertp_oda_id = ext.register_oda(12, false, 0x4BD8, 0) + ext.set_oda_handler(_Ertp_oda_id, function () + local b = (_Ertp_toggle and 1 or 0) << 4 | string.byte(userdata.get_offset(USERDATA_RTP_OFFSET+7, 1)) << 3 + local data_0 = userdata.get_offset(USERDATA_RTP_OFFSET+8, 3) + local data_1 = userdata.get_offset(USERDATA_RTP_OFFSET+11, 3) b = b | (string.byte(data_0, 1) & 0xf8) >> 3 local c = (string.byte(data_0, 1) & 0x7) << 13 @@ -52,46 +52,44 @@ local function init_ertp() end end -function set_rds_rtp_meta(ertp, running) +function rds.ext.set_rtp_meta(ertp, running) if ertp then if running and _Ertp_oda_id == nil then init_ertp() end - set_userdata_offset(USERDATA_RTP_OFFSET+7, 1, string.char(running and 1 or 0)) + userdata.set_offset(USERDATA_RTP_OFFSET+7, 1, string.char(running and 1 or 0)) else if running and _Rtp_oda_id == nil then init_rtp() end - set_userdata_offset(USERDATA_RTP_OFFSET, 1, string.char(running and 1 or 0)) + userdata.set_offset(USERDATA_RTP_OFFSET, 1, string.char(running and 1 or 0)) end end -function get_rds_rtp_meta(ertp) +function rds.ext.get_rtp_meta(ertp) local offset = ertp and (USERDATA_RTP_OFFSET+7) or USERDATA_RTP_OFFSET - return string.byte(get_userdata_offset(offset, 1)) ~= 0 + return string.byte(userdata.get_offset(offset, 1)) ~= 0 end -function toggle_rds_rtp(ertp) +function rds.ext.toggle_rtp(ertp) if ertp then _Ertp_toggle = not _Ertp_toggle else _Rtp_toggle = not _Rtp_toggle end end -function set_rds_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2) - set_rds_rtp_meta(ertp, true) - toggle_rds_rtp(ertp) - set_userdata_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6, string.char(t1, s1, l1, t2, s2, l2)) +function rds.ext.set_rtplus_tags(ertp, t1, s1, l1, t2, s2, l2) + rds.ext.set_rds_rtp_meta(ertp, true) + rds.ext.toggle_rds_rtp(ertp) + userdata.set_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6, string.char(t1, s1, l1, t2, s2, l2)) end -function get_rds_rtplus_tags(ertp) - return string.byte(get_userdata_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6), 1, 6) +function rds.ext.get_rtplus_tags(ertp) + return string.byte(userdata.get_offset(ertp and (USERDATA_RTP_OFFSET+8) or (USERDATA_RTP_OFFSET+1), 6), 1, 6) end function unregister_rtp(ertp) if ertp and _Ertp_oda_id ~= nil then - unregister_oda(_Ertp_oda_id) + ext.unregister_oda(_Ertp_oda_id) _Ertp_oda_id = nil elseif _Rtp_oda_id ~= nil then - unregister_oda(_Rtp_oda_id) + ext.unregister_oda(_Rtp_oda_id) _Rtp_oda_id = nil end end -local _old_on_state_rtp = on_state -function on_state() - if get_rds_rtp_meta(false) then init_rtp() end - if get_rds_rtp_meta(true) then init_ertp() end - if type(_old_on_state_rtp) == "function" then _old_on_state_rtp() end -end \ No newline at end of file +table.insert(on_states, function () + if rds.ext.get_rtp_meta(false) then init_rtp() end + if rds.ext.get_rtp_meta(true) then init_ertp() end +end) \ No newline at end of file diff --git a/scripts/99-data.lua b/scripts/99-data.lua index e862178..3f25cf2 100644 --- a/scripts/99-data.lua +++ b/scripts/99-data.lua @@ -40,52 +40,52 @@ function data_handle(data) data = data:lower() if data == "ver" then return string.format("rds95 core v. %s - (C) 2025 radio95 - lua parser\r\n", core_version) elseif data == "init" then - set_rds_program_defaults() + dp.set_rds_program_defaults() return "+\r\n" elseif data == "reset" then - reset_rds() + dp.reset_rds() return "+\r\n" - elseif data == "pi" then return string.format("PI=%s\r\n", string.format("%x", get_rds_pi())) - elseif data == "pty" then return string.format("PTY=%s\r\n", string.format("%d", get_rds_pty())) - elseif data == "ecc" then return string.format("ECC=%s\r\n", string.format("%x", get_rds_ecc())) - elseif data == "slcd" then return string.format("SLCD=%s\r\n", string.format("%x", get_rds_slc_data())) - elseif data == "ct" then return string.format("CT=%s\r\n", string.format("%d", (get_rds_ct() and 1 or 0))) - elseif data == "dpty" then return string.format("DPTY=%s\r\n", string.format("%d", (get_rds_dpty() and 1 or 0))) - elseif data == "tp" then return string.format("TP=%s\r\n", string.format("%d", (get_rds_tp() and 1 or 0))) - elseif data == "ta" then return string.format("TA=%s\r\n", string.format("%d", (get_rds_ta() and 1 or 0))) - elseif data == "rt1en" then return string.format("RT1EN=%s\r\n", string.format("%d", (get_rds_rt1_enabled() and 1 or 0))) - elseif data == "rt2en" then return string.format("RT2EN=%s\r\n", string.format("%d", (get_rds_rt2_enabled() and 1 or 0))) - elseif data == "ptynen" then return string.format("PTYNEN=%s\r\n", string.format("%d", (get_rds_ptyn_enabled() and 1 or 0))) - elseif data == "rttype" then return string.format("RTTYPE=%s\r\n", string.format("%d", get_rds_rt_type())) - elseif data == "rds2mod" then return string.format("RDS2MOD=%s\r\n", string.format("%d", get_rds2_mode())) + elseif data == "pi" then return string.format("PI=%s\r\n", string.format("%x", rds.get_pi())) + elseif data == "pty" then return string.format("PTY=%s\r\n", string.format("%d", rds.get_pty())) + elseif data == "ecc" then return string.format("ECC=%s\r\n", string.format("%x", rds.get_ecc())) + elseif data == "slcd" then return string.format("SLCD=%s\r\n", string.format("%x", rds.get_slc_data())) + elseif data == "ct" then return string.format("CT=%s\r\n", string.format("%d", (rds.get_ct() and 1 or 0))) + elseif data == "dpty" then return string.format("DPTY=%s\r\n", string.format("%d", (rds.get_dpty() and 1 or 0))) + elseif data == "tp" then return string.format("TP=%s\r\n", string.format("%d", (rds.get_tp() and 1 or 0))) + elseif data == "ta" then return string.format("TA=%s\r\n", string.format("%d", (rds.get_ta() and 1 or 0))) + elseif data == "rt1en" then return string.format("RT1EN=%s\r\n", string.format("%d", (rds.get_rt1_enabled() and 1 or 0))) + elseif data == "rt2en" then return string.format("RT2EN=%s\r\n", string.format("%d", (rds.get_rt2_enabled() and 1 or 0))) + elseif data == "ptynen" then return string.format("PTYNEN=%s\r\n", string.format("%d", (rds.get_ptyn_enabled() and 1 or 0))) + elseif data == "rttype" then return string.format("RTTYPE=%s\r\n", string.format("%d", rds.get_rt_type())) + elseif data == "rds2mod" then return string.format("RDS2MOD=%s\r\n", string.format("%d", rds.get_rds2_mode())) elseif data == "rdsgen" then return string.format("RDSGEN=%s\r\n", string.format("%d", get_rds_streams())) elseif data == "level" then return string.format("LEVEL=%s\r\n", string.format("%d", get_rds_level() * 255)) - elseif data == "link" then return string.format("LINK=%s\r\n", string.format("%d", (get_rds_link() and 1 or 0))) + elseif data == "link" then return string.format("LINK=%s\r\n", string.format("%d", (rds.get_link() and 1 or 0))) elseif data == "rtp" then - local t1, s1, l1, t2, s2, l2 = get_rds_rtplus_tags(false) + local t1, s1, l1, t2, s2, l2 = rds.ext.get_rtplus_tags(false) return string.format("RTP=%d,%d,%d,%d,%d,%d\r\n", t1, s1, l1, t2, s2, l2) elseif data == "ertp" then - local t1, s1, l1, t2, s2, l2 = get_rds_rtplus_tags(true) + local t1, s1, l1, t2, s2, l2 = rds.ext.get_rtplus_tags(true) return string.format("ERTP=%d,%d,%d,%d,%d,%d\r\n", t1, s1, l1, t2, s2, l2) elseif data == "rtprun" then - local running = get_rds_rtp_meta(false) + local running = rds.ext.get_rtp_meta(false) local f1 = 2 or (running and 1 or 0) return string.format("RTPRUN=%d\r\n", f1) elseif data == "ertprun" then - local running = get_rds_rtp_meta(true) + local running = rds.ext.get_rtp_meta(true) local f1 = 2 or (running and 1 or 0) return string.format("ERTPRUN=%d\r\n", f1) - elseif data == "lps" then return string.format("LPS=%s\r\n", get_rds_lps()) - elseif data == "ert" then return string.format("ERT=%s\r\n", get_rds_ert()) - elseif data == "grpseq" then return string.format("GRPSEQ=%s\r\n", get_rds_grpseq()) - elseif data == "grpseq2" then return string.format("GRPSEQ2=%s\r\n", get_rds_grpseq2()) + elseif data == "lps" then return string.format("LPS=%s\r\n", rds.get_lps()) + elseif data == "ert" then return string.format("ERT=%s\r\n", rds.ext.get_ert()) + elseif data == "grpseq" then return string.format("GRPSEQ=%s\r\n", rds.get_grpseq()) + elseif data == "grpseq2" then return string.format("GRPSEQ2=%s\r\n", rds.get_grpseq2()) else local eon_cmd, eon_num = data:match("^eon(%d+)([a-z]+)$") if eon_cmd then local eon_idx = tonumber(eon_cmd) - if not eon_idx or eon_idx < 1 or eon_idx > eon_count then return "?\r\n" end + if not eon_idx or eon_idx < 1 or eon_idx > rds.eon_count then return "?\r\n" end eon_idx = eon_idx - 1 - local enabled, pi, tp, ta, pty, ps, afs, data_val = get_rds_eon(eon_idx) + local enabled, pi, tp, ta, pty, ps, afs, data_val = rds.get_eon(eon_idx) if eon_num == "en" then return string.format("EON%dEN=%d\r\n", eon_idx + 1, enabled and 1 or 0) elseif eon_num == "pi" then return string.format("EON%dPI=%x\r\n", eon_idx + 1, pi) @@ -106,48 +106,48 @@ function data_handle(data) local eon_num, eon_type = cmd:match("^eon(%d+)([a-z]+)$") if eon_num then local eon_idx = tonumber(eon_num) - if not eon_idx or eon_idx < 1 or eon_idx > eon_count then return "?\r\n" end + if not eon_idx or eon_idx < 1 or eon_idx > rds.eon_count then return "?\r\n" end eon_idx = eon_idx - 1 - local enabled, pi, tp, ta, pty, ps, afs, data_val = get_rds_eon(eon_idx) + local enabled, pi, tp, ta, pty, ps, afs, data_val = rds.get_eon(eon_idx) if eon_type == "en" then local en_val = tonumber(value) if not en_val then return "-\r\n" end enabled = (en_val ~= 0) - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val) return "+\r\n" elseif eon_type == "pi" then local pi_val = tonumber(value, 16) if not pi_val then return "-\r\n" end - set_rds_eon(eon_idx, enabled, pi_val, tp, ta, pty, ps, afs, data_val) + rds.set_eon(eon_idx, enabled, pi_val, tp, ta, pty, ps, afs, data_val) return "+\r\n" elseif eon_type == "ps" then local ps_val = value:sub(1, 24) - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps_val, afs, data_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps_val, afs, data_val) return "+\r\n" elseif eon_type == "pty" then local pty_val = tonumber(value) if not pty_val then return "-\r\n" end - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty_val, ps, afs, data_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty_val, ps, afs, data_val) return "+\r\n" elseif eon_type == "ta" then if not enabled or not tp then return "-\r\n" end local ta_val = tonumber(value) if not ta_val then return "-\r\n" end ta = (ta_val ~= 0) - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val) - if ta then set_rds_ta(true) end + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val) + if ta then rds.set_ta(true) end return "+\r\n" elseif eon_type == "tp" then local tp_val = tonumber(value) if not tp_val then return "-\r\n" end tp = (tp_val ~= 0) - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, data_val) return "+\r\n" elseif eon_type == "af" then local af_table = {} if value == "" or value == "0" then - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, {}, data_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, {}, data_val) return "+\r\n" end for freq_str in value:gmatch("([^,]+)") do @@ -156,12 +156,12 @@ function data_handle(data) else return "-\r\n" end end if #af_table > 25 then return "-\r\n" end - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, af_table, data_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, af_table, data_val) return "+\r\n" elseif eon_type == "dt" then local dt_val = tonumber(value, 16) if not dt_val then return "-\r\n" end - set_rds_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, dt_val) + rds.set_eon(eon_idx, enabled, pi, tp, ta, pty, ps, afs, dt_val) return "+\r\n" else return "?\r\n" end end @@ -179,7 +179,7 @@ function data_handle(data) end if #groups > 8 or #groups == 0 then return "-\r\n" end - set_rds_udg(xy, groups) + rds.set_udg(xy, groups) return "+\r\n" end if udg2_num then @@ -193,7 +193,7 @@ function data_handle(data) end if #groups > 8 or #groups == 0 then return "-" end - set_rds_udg2(xy, groups) + rds.set_udg2(xy, groups) return "+\r\n" end @@ -201,67 +201,67 @@ function data_handle(data) local pi = tonumber(value, 16) if not pi then return "-\r\n" end if (pi & 0xF000) == 0 then return "-\r\n" end - set_rds_pi(pi) + rds.set_pi(pi) return "+\r\n" elseif cmd == "ecc" then local ecc = tonumber(value, 16) if not ecc then return "-\r\n" end - set_rds_ecc(ecc) + rds.set_ecc(ecc) return "+\r\n" elseif cmd == "pty" then local pty = tonumber(value) if not pty then return "-\r\n" end - set_rds_pty(pty) + rds.set_pty(pty) return "+\r\n" elseif cmd == "slcd" then local slc_data = tonumber(value, 16) if not slc_data then return "-\r\n" end - set_rds_slc_data(slc_data) + rds.set_slc_data(slc_data) return "+\r\n" elseif cmd == "ct" then local ct = tonumber(value) if not ct then return "-\r\n" end - set_rds_ct(ct ~= 0) + rds.set_ct(ct ~= 0) return "+\r\n" elseif cmd == "dpty" then local dpty = tonumber(value) if not dpty then return "-\r\n" end - set_rds_dpty(dpty ~= 0) + rds.set_dpty(dpty ~= 0) return "+\r\n" elseif cmd == "tp" then local tp = tonumber(value) if not tp then return "-\r\n" end - set_rds_tp(tp ~= 0) + rds.set_tp(tp ~= 0) return "+\r\n" elseif cmd == "ta" then local ta = tonumber(value) if not ta then return "-\r\n" end - set_rds_ta(ta ~= 0) + rds.set_ta(ta ~= 0) return "+\r\n" elseif cmd == "rt1en" then local en = tonumber(value) if not en then return "-\r\n" end - set_rds_rt1_enabled(en ~= 0) + rds.set_rt1_enabled(en ~= 0) return "+\r\n" elseif cmd == "rt2en" then local en = tonumber(value) if not en then return "-\r\n" end - set_rds_rt2_enabled(en ~= 0) + rds.set_rt2_enabled(en ~= 0) return "+\r\n" elseif cmd == "ptynen" then local en = tonumber(value) if not en then return "-\r\n" end - set_rds_ptyn_enabled(en ~= 0) + rds.set_ptyn_enabled(en ~= 0) return "+\r\n" elseif cmd == "rttype" then local type = tonumber(value) if not type then return "-\r\n" end - set_rds_rt_type(type) + rds.set_rt_type(type) return "+\r\n" elseif cmd == "rds2mod" then local type = tonumber(value) if not type then return "-\r\n" end - set_rds2_mode(type) + rds.set_rds2_mode(type) return "+\r\n" elseif cmd == "rdsgen" then local type = tonumber(value) @@ -269,42 +269,42 @@ function data_handle(data) set_rds_streams(type) return "+\r\n" elseif cmd == "ptyn" then - set_rds_ptyn(value) + rds.set_ptyn(value) return "+\r\n" elseif cmd == "ps" then - set_rds_ps(value) + rds.set_ps(value) return "+\r\n" elseif cmd == "tps" then - set_rds_tps(value) + rds.set_tps(value) return "+\r\n" elseif cmd == "rt1" or cmd == "text" then - set_rds_rt1(value) + rds.set_rt1(value) return "+\r\n" elseif cmd == "rt2" then - set_rds_rt2(value) + rds.set_rt2(value) return "+\r\n" elseif cmd == "lps" then - set_rds_lps(value) + rds.set_lps(value) return "+\r\n" elseif cmd == "ert" then - set_rds_ert(value) + rds.ext.set_ert(value) return "+\r\n" elseif cmd == "link" then local link = tonumber(value) if not link then return "-\r\n" end - set_rds_link(link ~= 0) + rds.set_link(link ~= 0) return "+\r\n" elseif cmd == "rtper" then local period = tonumber(value) if not period then return "-\r\n" end - set_rds_rt_switching_period(period*60) + rds.set_rt_switching_period(period*60) return "+\r\n" elseif cmd == "program" then local program = tonumber(value) if not program then return "-\r\n" end - if program < 1 or program > max_programs then return "-\r\n" end - set_rds_program(program-1) - set_rds_ta(false) + if program < 1 or program > dp.max_programs then return "-\r\n" end + dp.set_rds_program(program-1) + rds.set_ta(false) return "+\r\n" elseif cmd == "level" then local level = tonumber(value) @@ -314,20 +314,20 @@ function data_handle(data) elseif cmd == "dttmout" then local timeout = tonumber(value) if not timeout then return "-\r\n" end - set_rds_rt_text_timeout(timeout*60) + rds.set_rt_text_timeout(timeout*60) return "+\r\n" elseif cmd == "grpseq" then - set_rds_grpseq(value) + rds.set_grpseq(value) return "+\r\n" elseif cmd == "grpseq2" then - set_rds_grpseq2(value) + rds.set_grpseq2(value) return "+\r\n" elseif cmd == "rtp" or cmd == "ertp" then local is_ertp = (cmd == "ertp") local t1, s1, l1, t2, s2, l2 = value:match("(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)") if not l2 then return "-\r\n" end - set_rds_rtplus_tags( + rds.ext.set_rtplus_tags( is_ertp, ---@diagnostic disable-next-line: param-type-mismatch tonumber(t1), tonumber(s1), tonumber(l1), tonumber(t2), tonumber(s2), tonumber(l2) @@ -336,13 +336,13 @@ function data_handle(data) elseif cmd == "g" then local a, b, c, d = value:match("^(%x%x%x%x)(%x%x%x%x)(%x%x%x%x)(%x%x%x%x)$") if a and b and c and d then - put_rds2_custom_group(tonumber(a, 16), tonumber(b, 16), tonumber(c, 16), tonumber(d, 16)) + rds.put_rds2_custom_group(tonumber(a, 16), tonumber(b, 16), tonumber(c, 16), tonumber(d, 16)) return "+\r\n" end local b1, c1, d1 = value:match("^(%x%x%x%x)(%x%x%x%x)(%x%x%x%x)$") if b1 and c1 and d1 then - put_rds_custom_group(tonumber(b1, 16), tonumber(c1, 16), tonumber(d1, 16)) + rds.put_custom_group(tonumber(b1, 16), tonumber(c1, 16), tonumber(d1, 16)) return "+\r\n" end @@ -357,14 +357,14 @@ function data_handle(data) local f2 = tonumber(f2_str) or 0 local running = (f1 & 1) ~= 0 - set_rds_rtp_meta(is_ertp, running) - if f2 ~= 0 then toggle_rds_rtp(is_ertp) end + rds.ext.set_rtp_meta(is_ertp, running) + if f2 ~= 0 then rds.ext.toggle_rtp(is_ertp) end return "+\r\n" elseif cmd == "af" then local af_table = {} if value == "" or value == "0" then - set_rds_af_group0({}) + rds.set_af({}) return "+\r\n" end @@ -376,13 +376,13 @@ function data_handle(data) if #af_table > 25 then return "-\r\n" end - set_rds_af_group0(af_table) + rds.set_af(af_table) return "+\r\n" elseif cmd == "afo" then local af_table = {} if value == "" or value == "0" then - set_rds_af_oda({}) + rds.ext.set_af_oda({}) return "+\r\n" end @@ -394,7 +394,7 @@ function data_handle(data) if #af_table > 25 then return "-\r\n" end - set_rds_af_oda(af_table) + rds.ext.set_af_oda(af_table) return "+\r\n" else return "?\r\n" diff --git a/src/lua_api.c b/src/lua_api.c new file mode 100644 index 0000000..2496d8c --- /dev/null +++ b/src/lua_api.c @@ -0,0 +1,425 @@ +#include "lua_api.h" +#include "lua_rds.h" + +static int in_set_defaults = 0; + +extern lua_State *L; +extern RDSModulator* mod; +extern uint8_t unload_refs[33]; + +int lua_get_userdata(lua_State *localL) { + lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data, LUA_USER_DATA); + return 1; +} +int lua_get_userdata_offset(lua_State *localL) { + uint16_t offset = luaL_checkinteger(localL, 1); + uint16_t size = luaL_checkinteger(localL, 2); + if((offset+size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit"); + lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data[offset], size); + return 1; +} +int lua_set_userdata(lua_State *localL) { + size_t len; + const char *data = luaL_checklstring(localL, 1, &len); + if(len > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit"); + memset(mod->enc->data[mod->enc->program].lua_data, 0, LUA_USER_DATA); + memcpy(mod->enc->data[mod->enc->program].lua_data, data, len); + + return 0; +} +int lua_set_userdata_offset(lua_State *localL) { + uint16_t offset = luaL_checkinteger(localL, 1); + uint16_t size = luaL_checkinteger(localL, 2); + + size_t len; + const char *data = luaL_checklstring(localL, 3, &len); + if(len > size || (offset + size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit"); + memset(mod->enc->data[mod->enc->program].lua_data + offset, 0, size); + memcpy(mod->enc->data[mod->enc->program].lua_data + offset, data, len); + + return 0; +} + +int lua_force_save(lua_State *localL) { + (void)localL; + encoder_saveToFile(mod->enc); + Modulator_saveToFile(&mod->params); + return 0; +} + +int lua_set_rds_program_defaults(lua_State *localL) { + (void)localL; + if (in_set_defaults) { + fprintf(stderr, "Warning: Recursive call to lua_set_rds_program_defaults blocked\n"); + return 0; + } + in_set_defaults = 1; + for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]); + unload_refs[0] = 1; + set_rds_defaults(mod->enc, mod->enc->program); + lua_call_tfunction_nolock("on_init"); + lua_call_tfunction_nolock("on_state"); + in_set_defaults = 0; + return 0; +} + +int lua_reset_rds(lua_State *localL) { + (void)localL; + for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]); + unload_refs[0] = 1; + encoder_saveToFile(mod->enc); + Modulator_saveToFile(&mod->params); + + encoder_loadFromFile(mod->enc); + for(int i = 0; i < PROGRAMS; i++) reset_rds_state(mod->enc, i); + Modulator_loadFromFile(&mod->params); + lua_call_tfunction_nolock("on_state"); + return 0; +} + +#define BOOL_SETTER(name) \ +int lua_set_rds_##name(lua_State *localL) { \ + if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); \ + mod->enc->data[mod->enc->program].name = lua_toboolean(localL, 1); \ + return 0; \ +} +#define INT_SETTER(name) \ +int lua_set_rds_##name(lua_State *localL) { \ + mod->enc->data[mod->enc->program].name = luaL_checkinteger(localL, 1); \ + return 0; \ +} +#define STR_SETTER(name, function) \ +int lua_set_rds_##name(lua_State *localL) { \ + const char* str = luaL_checklstring(localL, 1, NULL); \ + function(mod->enc, convert_to_rdscharset(str)); \ + return 0; \ +} +#define STR_RAW_SETTER(name, function) \ +int lua_set_rds_##name(lua_State *localL) { \ + const char* str = luaL_checklstring(localL, 1, NULL); \ + function(mod->enc, str); \ + return 0; \ +} +#define AF_SETTER(name, af_field, af_struct, add_func) \ +int lua_set_rds_##name(lua_State *localL) { \ + luaL_checktype(localL, 1, LUA_TTABLE); \ + \ + int n = lua_rawlen(localL, 1); \ + if (n == 0) { \ + memset(&(mod->enc->data[mod->enc->program].af_field), 0, sizeof(af_struct)); \ + return 0; \ + } \ + if(n > 25) return luaL_error(localL, "table length over 25"); \ + \ + af_struct new_af; \ + memset(&new_af, 0, sizeof(af_struct)); \ + \ + for (int i = 1; i <= n; i++) { \ + lua_rawgeti(localL, 1, i); \ + if (lua_isnumber(localL, -1)) add_func(&new_af, lua_tonumber(localL, -1)); \ + else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1)); \ + lua_pop(localL, 1); \ + } \ + memcpy(&(mod->enc->data[mod->enc->program].af_field), &new_af, sizeof(new_af)); \ + \ + return 0; \ +} + +#define INT_GETTER(name) \ +int lua_get_rds_##name(lua_State *localL) { \ + lua_pushinteger(localL, mod->enc->data[mod->enc->program].name); \ + return 1; \ +} +#define BOOL_GETTER(name) \ +int lua_get_rds_##name(lua_State *localL) { \ + lua_pushboolean(localL, mod->enc->data[mod->enc->program].name); \ + return 1; \ +} +#define STR_RAW_GETTER(name, length) \ +int lua_get_rds_##name(lua_State *localL) { \ + lua_pushlstring(localL, mod->enc->data[mod->enc->program].name, length); \ + return 1; \ +} +INT_SETTER(pi) +INT_GETTER(pi) + +INT_SETTER(pty) +INT_GETTER(pty) + +INT_SETTER(ecc) +INT_GETTER(ecc) + +INT_SETTER(slc_data) +INT_GETTER(slc_data) + +BOOL_SETTER(ct) +BOOL_GETTER(ct) + +BOOL_SETTER(dpty) +BOOL_GETTER(dpty) + +BOOL_SETTER(tp) +BOOL_GETTER(tp) + +BOOL_SETTER(ta) +BOOL_GETTER(ta) + +BOOL_SETTER(rt1_enabled) +BOOL_GETTER(rt1_enabled) + +BOOL_SETTER(rt2_enabled) +BOOL_GETTER(rt2_enabled) + +BOOL_SETTER(ptyn_enabled) +BOOL_GETTER(ptyn_enabled) + +INT_SETTER(rt_type) +INT_GETTER(rt_type) + +int lua_set_rds2_mode(lua_State *localL) { + mod->enc->encoder_data.rds2_mode = luaL_checkinteger(localL, 1); + return 0; +} + +int lua_get_rds2_mode(lua_State *localL) { + lua_pushinteger(localL, mod->enc->encoder_data.rds2_mode); + return 1; +} + +int lua_set_rds_streams(lua_State *localL) { + mod->params.rdsgen = luaL_checkinteger(localL, 1); + return 0; +} + +int lua_get_rds_streams(lua_State *localL) { + lua_pushinteger(localL, mod->params.rdsgen); + return 1; +} + +int lua_set_rds_link(lua_State *localL) { + if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); + mod->enc->state[mod->enc->program].eon_linkage = lua_toboolean(localL, 1); + return 0; +} + +int lua_get_rds_link(lua_State *localL) { + lua_pushboolean(localL, mod->enc->state[mod->enc->program].eon_linkage); + return 1; +} + +int lua_set_rds_program(lua_State *localL) { + int program = luaL_checkinteger(localL, 1); + if(program >= PROGRAMS) program = (PROGRAMS-1); + if(program < 0) program = 0; + + if(mod->enc->program == program) return 0; + + mod->enc->data[mod->enc->program].ta = 0; + mod->enc->data[(uint8_t)program].ta = 0; + mod->enc->program = (uint8_t)program; + return 0; +} + +int lua_get_rds_program(lua_State *localL) { + lua_pushinteger(localL, mod->enc->program); + return 1; +} + +int lua_set_rds_rt_switching_period(lua_State *localL) { + mod->enc->data[mod->enc->program].rt_switching_period = luaL_checkinteger(localL, 1); + mod->enc->state[mod->enc->program].rt_switching_period_state = mod->enc->data[mod->enc->program].rt_switching_period; + return 0; +} +INT_GETTER(rt_switching_period) + +int lua_set_rds_rt_text_timeout(lua_State *localL) { + mod->enc->data[mod->enc->program].rt_text_timeout = luaL_checkinteger(localL, 1); + mod->enc->state[mod->enc->program].rt_text_timeout_state = mod->enc->data[mod->enc->program].rt_text_timeout; + return 0; +} +INT_GETTER(rt_text_timeout) + +int lua_set_rds_level(lua_State *localL) { + mod->params.level = luaL_checknumber(localL, 1); + return 0; +} + +int lua_get_rds_level(lua_State *localL) { + lua_pushnumber(localL, mod->params.level); + return 1; +} + +int lua_put_rds_custom_group(lua_State *localL) { + mod->enc->state[mod->enc->program].custom_group[0] = 1; + mod->enc->state[mod->enc->program].custom_group[1] = luaL_checkinteger(localL, 1); + mod->enc->state[mod->enc->program].custom_group[2] = luaL_checkinteger(localL, 2); + mod->enc->state[mod->enc->program].custom_group[3] = luaL_checkinteger(localL, 3); + return 0; +} + +int lua_put_rds2_custom_group(lua_State *localL) { + mod->enc->state[mod->enc->program].custom_group2[0] = 1; + mod->enc->state[mod->enc->program].custom_group2[1] = luaL_checkinteger(localL, 1); + mod->enc->state[mod->enc->program].custom_group2[2] = luaL_checkinteger(localL, 2); + mod->enc->state[mod->enc->program].custom_group2[3] = luaL_checkinteger(localL, 3); + mod->enc->state[mod->enc->program].custom_group2[4] = luaL_checkinteger(localL, 4); + return 0; +} + +STR_SETTER(ptyn, set_rds_ptyn) +STR_SETTER(ps, set_rds_ps) +STR_SETTER(tps, set_rds_tps) +STR_SETTER(rt1, set_rds_rt1) +STR_SETTER(rt2, set_rds_rt2) +STR_SETTER(default_rt, set_rds_default_rt) + +STR_RAW_SETTER(lps, set_rds_lps) +STR_RAW_GETTER(lps, LPS_LENGTH) + +STR_RAW_SETTER(grp_sqc_rds2, set_rds_grpseq2) +STR_RAW_GETTER(grp_sqc_rds2, 32) + +int lua_set_rds_grp_sqc(lua_State *localL) { + const char* str = luaL_checklstring(localL, 1, NULL); + if(_strnlen(str, 2) < 1) set_rds_grpseq(mod->enc, DEFAULT_GRPSQC); + else set_rds_grpseq(mod->enc, str); + return 0; +} + +STR_RAW_GETTER(grp_sqc, 32) + +AF_SETTER(af_group0, af, RDSAFs, add_rds_af) + +int lua_set_rds_eon(lua_State *localL) { + int eon = luaL_checkinteger(localL, 1); + if(eon >= EONs) return luaL_error(localL, "eon index exceeded"); + if (!lua_isboolean(localL, 2)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 2)); + if (!lua_isboolean(localL, 4)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 4)); + if (!lua_isboolean(localL, 5)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 5)); + luaL_checktype(localL, 8, LUA_TTABLE); + mod->enc->data[mod->enc->program].eon[eon].enabled = lua_toboolean(localL, 2); + mod->enc->data[mod->enc->program].eon[eon].pi = luaL_checkinteger(localL, 3); + mod->enc->data[mod->enc->program].eon[eon].tp = lua_toboolean(localL, 4); + mod->enc->data[mod->enc->program].eon[eon].ta = lua_toboolean(localL, 5); + mod->enc->data[mod->enc->program].eon[eon].pty = luaL_checkinteger(localL, 6); + _strncpy(mod->enc->data[mod->enc->program].eon[eon].ps, luaL_checklstring(localL, 7, NULL), 8); + + int n = lua_rawlen(localL, 8); + if (n == 0) { + memset(&(mod->enc->data[mod->enc->program].eon[eon].af), 0, sizeof(RDSAFs)); + return 0; + } + if(n > 25) return luaL_error(localL, "table length over 25"); + + RDSAFs new_af; + memset(&new_af, 0, sizeof(RDSAFs)); + + for (int i = 1; i <= n; i++) { + lua_rawgeti(localL, 8, i); + if (lua_isnumber(localL, -1)) add_rds_af(&new_af, lua_tonumber(localL, -1)); + else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1)); + lua_pop(localL, 1); + } + memcpy(&(mod->enc->data[mod->enc->program].eon[eon].af), &new_af, sizeof(new_af)); + + mod->enc->data[mod->enc->program].eon[eon].data = luaL_checkinteger(localL, 9); + return 0; +} + +int lua_get_rds_eon(lua_State *localL) { + int eon = luaL_checkinteger(localL, 1); + if(eon >= EONs) return luaL_error(localL, "eon index exceeded"); + lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].enabled); + lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pi); + lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].tp); + lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].ta); + lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pty); + lua_pushlstring(localL, mod->enc->data[mod->enc->program].eon[eon].ps, 8); + lua_createtable(localL, 0, 0); // don't have decoding for AF, so just return empty table + lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].data); + return 8; +} + +int lua_set_rds_udg(lua_State *localL) { + if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); + int xy = lua_toboolean(localL, 1); + luaL_checktype(localL, 2, LUA_TTABLE); + int n = lua_rawlen(localL, 2); + if(n > 8) return luaL_error(localL, "table length over 8"); + + uint16_t blocks[8][3] = {0}; + + for (int i = 1; i <= n; i++) { + lua_rawgeti(localL, 2, i); + if(lua_istable(localL, -1)) { + int n2 = lua_rawlen(localL, -1); + if(n2 > 3) return luaL_error(localL, "table length over 3"); + for(int j = 1; j <= n2; j++) { + lua_rawgeti(localL, -1, j); + if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1); + else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1)); + lua_pop(localL, 1); + } + } + else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1)); + lua_pop(localL, 1); + } + + if(xy) { + memcpy(&(mod->enc->data[mod->enc->program].udg2), blocks, n * sizeof(uint16_t[3])); + mod->enc->data[mod->enc->program].udg2_len = n; + } else { + memcpy(&(mod->enc->data[mod->enc->program].udg1), blocks, n * sizeof(uint16_t[3])); + mod->enc->data[mod->enc->program].udg1_len = n; + } + + return 0; +} + +int lua_set_rds_udg2(lua_State *localL) { + if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); + int xy = lua_toboolean(localL, 1); + luaL_checktype(localL, 2, LUA_TTABLE); + int n = lua_rawlen(localL, 2); + if(n > 8) return luaL_error(localL, "table length over 8"); + + uint16_t blocks[8][4] = {0}; + + for (int i = 1; i <= n; i++) { + lua_rawgeti(localL, 2, i); + if(lua_istable(localL, -1)) { + int n2 = lua_rawlen(localL, -1); + if(n2 > 4) return luaL_error(localL, "table length over 4"); + for(int j = 1; j <= n2; j++) { + lua_rawgeti(localL, -1, j); + if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1); + else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1)); + lua_pop(localL, 1); + } + } + else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1)); + lua_pop(localL, 1); + } + + if(xy) { + memcpy(&(mod->enc->data[mod->enc->program].udg2_rds2), blocks, n * sizeof(uint16_t[4])); + mod->enc->data[mod->enc->program].udg2_len_rds2 = n; + } else { + memcpy(&(mod->enc->data[mod->enc->program].udg1_rds2), blocks, n * sizeof(uint16_t[4])); + mod->enc->data[mod->enc->program].udg1_len_rds2 = n; + } + + return 0; +} + +int lua_get_available_rds_streams(lua_State *localL) { + lua_pushinteger(localL, mod->num_streams); + return 1; +} + +int lua_crc16(lua_State *localL) { + size_t len; + const char* data = luaL_checklstring(localL, 1, &len); + lua_pushinteger(localL, crc16_ccitt(data, len)); + return 1; +} \ No newline at end of file diff --git a/src/lua_api.h b/src/lua_api.h new file mode 100644 index 0000000..788c5b2 --- /dev/null +++ b/src/lua_api.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include +#include +#include "rds.h" +#include "fs.h" +#include "modulator.h" + +int lua_get_userdata(lua_State *localL); +int lua_get_userdata_offset(lua_State *localL); +int lua_set_userdata(lua_State *localL); +int lua_set_userdata_offset(lua_State *localL); +int lua_force_save(lua_State *localL); +int lua_set_rds_program_defaults(lua_State *localL); +int lua_reset_rds(lua_State *localL); + +int lua_set_rds_pi(lua_State *localL); +int lua_get_rds_pi(lua_State *localL); + +int lua_set_rds_pty(lua_State *localL); +int lua_get_rds_pty(lua_State *localL); + +int lua_set_rds_ecc(lua_State *localL); +int lua_get_rds_ecc(lua_State *localL); + +int lua_set_rds_slc_data(lua_State *localL); +int lua_get_rds_slc_data(lua_State *localL); + +int lua_set_rds_ct(lua_State *localL); +int lua_get_rds_ct(lua_State *localL); + +int lua_set_rds_dpty(lua_State *localL); +int lua_get_rds_dpty(lua_State *localL); + +int lua_set_rds_tp(lua_State *localL); +int lua_get_rds_tp(lua_State *localL); + +int lua_set_rds_ta(lua_State *localL); +int lua_get_rds_ta(lua_State *localL); + +int lua_set_rds_rt1_enabled(lua_State *localL); +int lua_get_rds_rt1_enabled(lua_State *localL); + +int lua_set_rds_rt2_enabled(lua_State *localL); +int lua_get_rds_rt2_enabled(lua_State *localL); + +int lua_set_rds_ptyn_enabled(lua_State *localL); +int lua_get_rds_ptyn_enabled(lua_State *localL); + +int lua_set_rds_rt_type(lua_State *localL); +int lua_get_rds_rt_type(lua_State *localL); + +int lua_set_rds2_mode(lua_State *localL); +int lua_get_rds2_mode(lua_State *localL); + +int lua_set_rds_streams(lua_State *localL); +int lua_get_rds_streams(lua_State *localL); +int lua_get_available_rds_streams(lua_State *localL); + +int lua_set_rds_grp_sqc(lua_State *localL); +int lua_get_rds_grp_sqc(lua_State *localL); + +int lua_set_rds_grp_sqc_rds2(lua_State *localL); +int lua_get_rds_grp_sqc_rds2(lua_State *localL); + +int lua_set_rds_link(lua_State *localL); +int lua_get_rds_link(lua_State *localL); + +int lua_set_rds_program(lua_State *localL); +int lua_get_rds_program(lua_State *localL); + +int lua_set_rds_rt_switching_period(lua_State *localL); +int lua_get_rds_rt_switching_period(lua_State *localL); + +int lua_set_rds_rt_text_timeout(lua_State *localL); +int lua_get_rds_rt_text_timeout(lua_State *localL); + +int lua_set_rds_level(lua_State *localL); +int lua_get_rds_level(lua_State *localL); + +int lua_put_rds_custom_group(lua_State *localL); +int lua_put_rds2_custom_group(lua_State *localL); + +int lua_set_rds_lps(lua_State *localL); +int lua_get_rds_lps(lua_State *localL); +int lua_set_rds_af_group0(lua_State *localL); +int lua_set_rds_default_rt(lua_State *localL); +int lua_set_rds_rt2(lua_State *localL); +int lua_set_rds_rt1(lua_State *localL); +int lua_set_rds_tps(lua_State *localL); +int lua_set_rds_ps(lua_State *localL); +int lua_set_rds_ptyn(lua_State *localL); +int lua_set_rds_grp_sqc(lua_State *localL); + +int lua_set_rds_eon(lua_State *localL); +int lua_get_rds_eon(lua_State *localL); +int lua_set_rds_udg(lua_State *localL); +int lua_set_rds_udg2(lua_State *localL); + +int lua_crc16(lua_State *localL); \ No newline at end of file diff --git a/src/lua_rds.c b/src/lua_rds.c index 787fa7f..ff2b15b 100644 --- a/src/lua_rds.c +++ b/src/lua_rds.c @@ -1,418 +1,13 @@ #include "lua_rds.h" #include +#include "lua_api.h" -static RDSModulator* mod = NULL; -static lua_State *L = NULL; +RDSModulator* mod = NULL; +lua_State *L = NULL; static pthread_mutex_t lua_mutex; -static uint8_t unload_refs[33] = {LUA_REFNIL}; -static int in_set_defaults = 0; - -int lua_get_userdata(lua_State *localL) { - lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data, LUA_USER_DATA); - return 1; -} -int lua_get_userdata_offset(lua_State *localL) { - uint16_t offset = luaL_checkinteger(localL, 1); - uint16_t size = luaL_checkinteger(localL, 2); - if((offset+size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit"); - lua_pushlstring(localL, (const char*)&mod->enc->data[mod->enc->program].lua_data[offset], size); - return 1; -} -int lua_set_userdata(lua_State *localL) { - size_t len; - const char *data = luaL_checklstring(localL, 1, &len); - if(len > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit"); - memset(mod->enc->data[mod->enc->program].lua_data, 0, LUA_USER_DATA); - memcpy(mod->enc->data[mod->enc->program].lua_data, data, len); - - return 0; -} -int lua_set_userdata_offset(lua_State *localL) { - uint16_t offset = luaL_checkinteger(localL, 1); - uint16_t size = luaL_checkinteger(localL, 2); - - size_t len; - const char *data = luaL_checklstring(localL, 3, &len); - if(len > size || (offset + size) > LUA_USER_DATA) return luaL_error(localL, "data exceeds limit"); - memset(mod->enc->data[mod->enc->program].lua_data + offset, 0, size); - memcpy(mod->enc->data[mod->enc->program].lua_data + offset, data, len); - - return 0; -} - -int lua_force_save(lua_State *localL) { - (void)localL; - encoder_saveToFile(mod->enc); - Modulator_saveToFile(&mod->params); - return 0; -} - -int lua_set_rds_program_defaults(lua_State *localL) { - (void)localL; - if (in_set_defaults) { - fprintf(stderr, "Warning: Recursive call to lua_set_rds_program_defaults blocked\n"); - return 0; - } - in_set_defaults = 1; - for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]); - unload_refs[0] = 1; - set_rds_defaults(mod->enc, mod->enc->program); - lua_call_function_nolock("on_init"); - lua_call_function_nolock("on_state"); - in_set_defaults = 0; - return 0; -} - -int lua_reset_rds(lua_State *localL) { - (void)localL; - for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]); - unload_refs[0] = 1; - encoder_saveToFile(mod->enc); - Modulator_saveToFile(&mod->params); - - encoder_loadFromFile(mod->enc); - for(int i = 0; i < PROGRAMS; i++) reset_rds_state(mod->enc, i); - Modulator_loadFromFile(&mod->params); - lua_call_function_nolock("on_state"); - return 0; -} - -#define BOOL_SETTER(name) \ -int lua_set_rds_##name(lua_State *localL) { \ - if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); \ - mod->enc->data[mod->enc->program].name = lua_toboolean(localL, 1); \ - return 0; \ -} -#define INT_SETTER(name) \ -int lua_set_rds_##name(lua_State *localL) { \ - mod->enc->data[mod->enc->program].name = luaL_checkinteger(localL, 1); \ - return 0; \ -} -#define STR_SETTER(name, function) \ -int lua_set_rds_##name(lua_State *localL) { \ - const char* str = luaL_checklstring(localL, 1, NULL); \ - function(mod->enc, convert_to_rdscharset(str)); \ - return 0; \ -} -#define STR_RAW_SETTER(name, function) \ -int lua_set_rds_##name(lua_State *localL) { \ - const char* str = luaL_checklstring(localL, 1, NULL); \ - function(mod->enc, str); \ - return 0; \ -} -#define AF_SETTER(name, af_field, af_struct, add_func) \ -int lua_set_rds_##name(lua_State *localL) { \ - luaL_checktype(localL, 1, LUA_TTABLE); \ - \ - int n = lua_rawlen(localL, 1); \ - if (n == 0) { \ - memset(&(mod->enc->data[mod->enc->program].af_field), 0, sizeof(af_struct)); \ - return 0; \ - } \ - if(n > 25) return luaL_error(localL, "table length over 25"); \ - \ - af_struct new_af; \ - memset(&new_af, 0, sizeof(af_struct)); \ - \ - for (int i = 1; i <= n; i++) { \ - lua_rawgeti(localL, 1, i); \ - if (lua_isnumber(localL, -1)) add_func(&new_af, lua_tonumber(localL, -1)); \ - else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1)); \ - lua_pop(localL, 1); \ - } \ - memcpy(&(mod->enc->data[mod->enc->program].af_field), &new_af, sizeof(new_af)); \ - \ - return 0; \ -} - -#define INT_GETTER(name) \ -int lua_get_rds_##name(lua_State *localL) { \ - lua_pushinteger(localL, mod->enc->data[mod->enc->program].name); \ - return 1; \ -} -#define BOOL_GETTER(name) \ -int lua_get_rds_##name(lua_State *localL) { \ - lua_pushboolean(localL, mod->enc->data[mod->enc->program].name); \ - return 1; \ -} -#define STR_RAW_GETTER(name, length) \ -int lua_get_rds_##name(lua_State *localL) { \ - lua_pushlstring(localL, mod->enc->data[mod->enc->program].name, length); \ - return 1; \ -} -INT_SETTER(pi) -INT_GETTER(pi) - -INT_SETTER(pty) -INT_GETTER(pty) - -INT_SETTER(ecc) -INT_GETTER(ecc) - -INT_SETTER(slc_data) -INT_GETTER(slc_data) - -BOOL_SETTER(ct) -BOOL_GETTER(ct) - -BOOL_SETTER(dpty) -BOOL_GETTER(dpty) - -BOOL_SETTER(tp) -BOOL_GETTER(tp) - -BOOL_SETTER(ta) -BOOL_GETTER(ta) - -BOOL_SETTER(rt1_enabled) -BOOL_GETTER(rt1_enabled) - -BOOL_SETTER(rt2_enabled) -BOOL_GETTER(rt2_enabled) - -BOOL_SETTER(ptyn_enabled) -BOOL_GETTER(ptyn_enabled) - -INT_SETTER(rt_type) -INT_GETTER(rt_type) - -int lua_set_rds2_mode(lua_State *localL) { - mod->enc->encoder_data.rds2_mode = luaL_checkinteger(localL, 1); - return 0; -} -int lua_get_rds2_mode(lua_State *localL) { - lua_pushinteger(localL, mod->enc->encoder_data.rds2_mode); - return 1; -} -int lua_set_rds_streams(lua_State *localL) { - mod->params.rdsgen = luaL_checkinteger(localL, 1); - return 0; -} -int lua_get_rds_streams(lua_State *localL) { - lua_pushinteger(localL, mod->params.rdsgen); - return 1; -} - -int lua_set_rds_link(lua_State *localL) { - if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); - mod->enc->state[mod->enc->program].eon_linkage = lua_toboolean(localL, 1); - return 0; -} -int lua_get_rds_link(lua_State *localL) { - lua_pushboolean(localL, mod->enc->state[mod->enc->program].eon_linkage); - return 1; -} - -int lua_set_rds_program(lua_State *localL) { - int program = luaL_checkinteger(localL, 1); - if(program >= PROGRAMS) program = (PROGRAMS-1); - if(program < 0) program = 0; - - if(mod->enc->program == program) return 0; - - mod->enc->data[mod->enc->program].ta = 0; - mod->enc->data[(uint8_t)program].ta = 0; - mod->enc->program = (uint8_t)program; - return 0; -} -int lua_get_rds_program(lua_State *localL) { - lua_pushinteger(localL, mod->enc->program); - return 1; -} - -int lua_set_rds_rt_switching_period(lua_State *localL) { - mod->enc->data[mod->enc->program].rt_switching_period = luaL_checkinteger(localL, 1); - mod->enc->state[mod->enc->program].rt_switching_period_state = mod->enc->data[mod->enc->program].rt_switching_period; - return 0; -} -INT_GETTER(rt_switching_period) -int lua_set_rds_rt_text_timeout(lua_State *localL) { - mod->enc->data[mod->enc->program].rt_text_timeout = luaL_checkinteger(localL, 1); - mod->enc->state[mod->enc->program].rt_text_timeout_state = mod->enc->data[mod->enc->program].rt_text_timeout; - return 0; -} -INT_GETTER(rt_text_timeout) - -int lua_set_rds_level(lua_State *localL) { - mod->params.level = luaL_checknumber(localL, 1); - return 0; -} -int lua_get_rds_level(lua_State *localL) { - lua_pushnumber(localL, mod->params.level); - return 1; -} - -int lua_put_rds_custom_group(lua_State *localL) { - mod->enc->state[mod->enc->program].custom_group[0] = 1; - mod->enc->state[mod->enc->program].custom_group[1] = luaL_checkinteger(localL, 1); - mod->enc->state[mod->enc->program].custom_group[2] = luaL_checkinteger(localL, 2); - mod->enc->state[mod->enc->program].custom_group[3] = luaL_checkinteger(localL, 3); - return 0; -} -int lua_put_rds2_custom_group(lua_State *localL) { - mod->enc->state[mod->enc->program].custom_group2[0] = 1; - mod->enc->state[mod->enc->program].custom_group2[1] = luaL_checkinteger(localL, 1); - mod->enc->state[mod->enc->program].custom_group2[2] = luaL_checkinteger(localL, 2); - mod->enc->state[mod->enc->program].custom_group2[3] = luaL_checkinteger(localL, 3); - mod->enc->state[mod->enc->program].custom_group2[4] = luaL_checkinteger(localL, 4); - return 0; -} - -STR_SETTER(ptyn, set_rds_ptyn) -STR_SETTER(ps, set_rds_ps) -STR_SETTER(tps, set_rds_tps) -STR_SETTER(rt1, set_rds_rt1) -STR_SETTER(rt2, set_rds_rt2) -STR_SETTER(default_rt, set_rds_default_rt) - -STR_RAW_SETTER(lps, set_rds_lps) -STR_RAW_GETTER(lps, LPS_LENGTH) - -STR_RAW_SETTER(grp_sqc_rds2, set_rds_grpseq2) -STR_RAW_GETTER(grp_sqc_rds2, 32) - -int lua_set_rds_grp_sqc(lua_State *localL) { - const char* str = luaL_checklstring(localL, 1, NULL); - if(_strnlen(str, 2) < 1) set_rds_grpseq(mod->enc, DEFAULT_GRPSQC); - else set_rds_grpseq(mod->enc, str); - return 0; -} -STR_RAW_GETTER(grp_sqc, 32) - -AF_SETTER(af_group0, af, RDSAFs, add_rds_af) - -int lua_set_rds_eon(lua_State *localL) { - int eon = luaL_checkinteger(localL, 1); - if(eon >= EONs) return luaL_error(localL, "eon index exceeded"); - if (!lua_isboolean(localL, 2)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 2)); - if (!lua_isboolean(localL, 4)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 4)); - if (!lua_isboolean(localL, 5)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 5)); - luaL_checktype(localL, 8, LUA_TTABLE); - mod->enc->data[mod->enc->program].eon[eon].enabled = lua_toboolean(localL, 2); - mod->enc->data[mod->enc->program].eon[eon].pi = luaL_checkinteger(localL, 3); - mod->enc->data[mod->enc->program].eon[eon].tp = lua_toboolean(localL, 4); - mod->enc->data[mod->enc->program].eon[eon].ta = lua_toboolean(localL, 5); - mod->enc->data[mod->enc->program].eon[eon].pty = luaL_checkinteger(localL, 6); - _strncpy(mod->enc->data[mod->enc->program].eon[eon].ps, luaL_checklstring(localL, 7, NULL), 8); - - int n = lua_rawlen(localL, 8); - if (n == 0) { - memset(&(mod->enc->data[mod->enc->program].eon[eon].af), 0, sizeof(RDSAFs)); - return 0; - } - if(n > 25) return luaL_error(localL, "table length over 25"); - - RDSAFs new_af; - memset(&new_af, 0, sizeof(RDSAFs)); - - for (int i = 1; i <= n; i++) { - lua_rawgeti(localL, 8, i); - if (lua_isnumber(localL, -1)) add_rds_af(&new_af, lua_tonumber(localL, -1)); - else return luaL_error(localL, "number expected, got %s", luaL_typename(localL, -1)); - lua_pop(localL, 1); - } - memcpy(&(mod->enc->data[mod->enc->program].eon[eon].af), &new_af, sizeof(new_af)); - - mod->enc->data[mod->enc->program].eon[eon].data = luaL_checkinteger(localL, 9); - return 0; -} -int lua_get_rds_eon(lua_State *localL) { - int eon = luaL_checkinteger(localL, 1); - if(eon >= EONs) return luaL_error(localL, "eon index exceeded"); - lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].enabled); - lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pi); - lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].tp); - lua_pushboolean(localL, mod->enc->data[mod->enc->program].eon[eon].ta); - lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].pty); - lua_pushlstring(localL, mod->enc->data[mod->enc->program].eon[eon].ps, 8); - lua_createtable(localL, 0, 0); // don't have decoding for AF, so just return empty table - lua_pushinteger(localL, mod->enc->data[mod->enc->program].eon[eon].data); - return 8; -} - -int lua_set_rds_udg(lua_State *localL) { - if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); - int xy = lua_toboolean(localL, 1); - luaL_checktype(localL, 2, LUA_TTABLE); - int n = lua_rawlen(localL, 2); - if(n > 8) return luaL_error(localL, "table length over 8"); - - uint16_t blocks[8][3] = {0}; - - for (int i = 1; i <= n; i++) { - lua_rawgeti(localL, 2, i); - if(lua_istable(localL, -1)) { - int n2 = lua_rawlen(localL, -1); - if(n2 > 3) return luaL_error(localL, "table length over 3"); - for(int j = 1; j <= n2; j++) { - lua_rawgeti(localL, -1, j); - if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1); - else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1)); - lua_pop(localL, 1); - } - } - else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1)); - lua_pop(localL, 1); - } - - if(xy) { - memcpy(&(mod->enc->data[mod->enc->program].udg2), blocks, n * sizeof(uint16_t[3])); - mod->enc->data[mod->enc->program].udg2_len = n; - } else { - memcpy(&(mod->enc->data[mod->enc->program].udg1), blocks, n * sizeof(uint16_t[3])); - mod->enc->data[mod->enc->program].udg1_len = n; - } - - return 0; -} -int lua_set_rds_udg2(lua_State *localL) { - if (!lua_isboolean(localL, 1)) return luaL_error(localL, "boolean expected, got %s", luaL_typename(localL, 1)); - int xy = lua_toboolean(localL, 1); - luaL_checktype(localL, 2, LUA_TTABLE); - int n = lua_rawlen(localL, 2); - if(n > 8) return luaL_error(localL, "table length over 8"); - - uint16_t blocks[8][4] = {0}; - - for (int i = 1; i <= n; i++) { - lua_rawgeti(localL, 2, i); - if(lua_istable(localL, -1)) { - int n2 = lua_rawlen(localL, -1); - if(n2 > 4) return luaL_error(localL, "table length over 4"); - for(int j = 1; j <= n2; j++) { - lua_rawgeti(localL, -1, j); - if (lua_isinteger(localL, -1)) blocks[i-1][j-1] = lua_tointeger(localL, -1); - else return luaL_error(localL, "integer expected, got %s", luaL_typename(localL, -1)); - lua_pop(localL, 1); - } - } - else return luaL_error(localL, "table expected, got %s", luaL_typename(localL, -1)); - lua_pop(localL, 1); - } - - if(xy) { - memcpy(&(mod->enc->data[mod->enc->program].udg2_rds2), blocks, n * sizeof(uint16_t[4])); - mod->enc->data[mod->enc->program].udg2_len_rds2 = n; - } else { - memcpy(&(mod->enc->data[mod->enc->program].udg1_rds2), blocks, n * sizeof(uint16_t[4])); - mod->enc->data[mod->enc->program].udg1_len_rds2 = n; - } - - return 0; -} - -int lua_get_available_rds_streams(lua_State *localL) { - lua_pushinteger(localL, mod->num_streams); - return 1; -} - -int lua_crc16(lua_State *localL) { - size_t len; - const char* data = luaL_checklstring(localL, 1, &len); - lua_pushinteger(localL, crc16_ccitt(data, len)); - return 1; -} +uint8_t unload_refs[33] = {LUA_REFNIL}; +#define lua_registertotable(L,n,f) (lua_pushcfunction(L, (f)), lua_setfield(L, -2, (n))) void init_lua(RDSModulator* rds_mod) { static int mutex_initialized = 0; mod = rds_mod; @@ -430,109 +25,131 @@ void init_lua(RDSModulator* rds_mod) { lua_pushstring(L, VERSION); lua_setglobal(L, "core_version"); - lua_pushinteger(L, PROGRAMS); - lua_setglobal(L, "max_programs"); - lua_pushinteger(L, EONs); - lua_setglobal(L, "eon_count"); + + lua_newtable(L); + lua_setglobal(L, "ext"); + + lua_newtable(L); + lua_setglobal(L, "on_inits"); + lua_newtable(L); + lua_setglobal(L, "on_starts"); + lua_newtable(L); + lua_setglobal(L, "on_states"); + lua_newtable(L); + lua_setglobal(L, "ticks"); + + lua_newtable(L); + lua_registertotable(L, "get", lua_get_userdata); + lua_registertotable(L, "get_offset", lua_get_userdata_offset); + lua_registertotable(L, "set", lua_set_userdata); + lua_registertotable(L, "set_offset", lua_set_userdata_offset); lua_pushinteger(L, LUA_USER_DATA); - lua_setglobal(L, "user_data_len"); + lua_setfield(L, -2, "len"); + lua_setglobal(L, "userdata"); - lua_register(L, "set_rds_program_defaults", lua_set_rds_program_defaults); - lua_register(L, "reset_rds", lua_reset_rds); - lua_register(L, "force_save", lua_force_save); + lua_newtable(L); + lua_registertotable(L, "crc16", lua_crc16); + lua_registertotable(L, "force_save", lua_force_save); + lua_registertotable(L, "reset_rds", lua_reset_rds); + lua_registertotable(L, "set_rds_program_defaults", lua_set_rds_program_defaults); + lua_pushinteger(L, PROGRAMS); + lua_setfield(L, -2, "max_programs"); + lua_registertotable(L, "set_rds_program", lua_set_rds_program); + lua_registertotable(L, "get_rds_program", lua_get_rds_program); + lua_setglobal(L, "dp"); - lua_register(L, "set_rds_pi", lua_set_rds_pi); - lua_register(L, "get_rds_pi", lua_get_rds_pi); + lua_newtable(L); - lua_register(L, "set_rds_pty", lua_set_rds_pty); - lua_register(L, "get_rds_pty", lua_get_rds_pty); + lua_newtable(L); + lua_setfield(L, -2, "ext"); - lua_register(L, "set_rds_ecc", lua_set_rds_ecc); - lua_register(L, "get_rds_ecc", lua_get_rds_ecc); + lua_pushinteger(L, EONs); + lua_setfield(L, -2, "eon_count"); - lua_register(L, "set_rds_slc_data", lua_set_rds_slc_data); - lua_register(L, "get_rds_slc_data", lua_get_rds_slc_data); + lua_registertotable(L, "set_pi", lua_set_rds_pi); + lua_registertotable(L, "get_pi", lua_get_rds_pi); - lua_register(L, "set_rds_ct", lua_set_rds_ct); - lua_register(L, "get_rds_ct", lua_get_rds_ct); + lua_registertotable(L, "set_pty", lua_set_rds_pty); + lua_registertotable(L, "get_pty", lua_get_rds_pty); - lua_register(L, "set_rds_dpty", lua_set_rds_dpty); - lua_register(L, "get_rds_dpty", lua_get_rds_dpty); + lua_registertotable(L, "set_ecc", lua_set_rds_ecc); + lua_registertotable(L, "get_ecc", lua_get_rds_ecc); - lua_register(L, "set_rds_tp", lua_set_rds_tp); - lua_register(L, "get_rds_tp", lua_get_rds_tp); + lua_registertotable(L, "set_slc_data", lua_set_rds_slc_data); + lua_registertotable(L, "get_slc_data", lua_get_rds_slc_data); - lua_register(L, "set_rds_ta", lua_set_rds_ta); - lua_register(L, "get_rds_ta", lua_get_rds_ta); + lua_registertotable(L, "set_ct", lua_set_rds_ct); + lua_registertotable(L, "get_ct", lua_get_rds_ct); - lua_register(L, "set_rds_rt1_enabled", lua_set_rds_rt1_enabled); - lua_register(L, "get_rds_rt1_enabled", lua_get_rds_rt1_enabled); + lua_registertotable(L, "set_dpty", lua_set_rds_dpty); + lua_registertotable(L, "get_dpty", lua_get_rds_dpty); - lua_register(L, "set_rds_rt2_enabled", lua_set_rds_rt2_enabled); - lua_register(L, "get_rds_rt2_enabled", lua_get_rds_rt2_enabled); + lua_registertotable(L, "set_tp", lua_set_rds_tp); + lua_registertotable(L, "get_tp", lua_get_rds_tp); - lua_register(L, "set_rds_ptyn_enabled", lua_set_rds_ptyn_enabled); - lua_register(L, "get_rds_ptyn_enabled", lua_get_rds_ptyn_enabled); + lua_registertotable(L, "set_ta", lua_set_rds_ta); + lua_registertotable(L, "get_ta", lua_get_rds_ta); - lua_register(L, "set_rds_rt_type", lua_set_rds_rt_type); - lua_register(L, "get_rds_rt_type", lua_get_rds_rt_type); + lua_registertotable(L, "set_rt1_enabled", lua_set_rds_rt1_enabled); + lua_registertotable(L, "get_rt1_enabled", lua_get_rds_rt1_enabled); - lua_register(L, "set_rds2_mode", lua_set_rds2_mode); - lua_register(L, "get_rds2_mode", lua_get_rds2_mode); + lua_registertotable(L, "set_rt2_enabled", lua_set_rds_rt2_enabled); + lua_registertotable(L, "get_rt2_enabled", lua_get_rds_rt2_enabled); + + lua_registertotable(L, "set_ptyn_enabled", lua_set_rds_ptyn_enabled); + lua_registertotable(L, "get_ptyn_enabled", lua_get_rds_ptyn_enabled); + + lua_registertotable(L, "set_rt_type", lua_set_rds_rt_type); + lua_registertotable(L, "get_rt_type", lua_get_rds_rt_type); + + lua_registertotable(L, "set_rds2_mode", lua_set_rds2_mode); + lua_registertotable(L, "get_rds2_mode", lua_get_rds2_mode); + + lua_registertotable(L, "set_link", lua_set_rds_link); + lua_registertotable(L, "get_link", lua_get_rds_link); + + lua_registertotable(L, "set_rt_switching_period", lua_set_rds_rt_switching_period); + lua_registertotable(L, "get_rt_switching_period", lua_get_rds_rt_switching_period); + + lua_registertotable(L, "set_rt_text_timeout", lua_set_rds_rt_text_timeout); + lua_registertotable(L, "get_rt_text_timeout", lua_get_rds_rt_text_timeout); + + lua_registertotable(L, "set_ptyn", lua_set_rds_ptyn); + lua_registertotable(L, "set_ps", lua_set_rds_ps); + lua_registertotable(L, "set_tps", lua_set_rds_tps); + lua_registertotable(L, "set_rt1", lua_set_rds_rt1); + lua_registertotable(L, "set_rt2", lua_set_rds_rt2); + lua_registertotable(L, "set_default_rt", lua_set_rds_default_rt); + + lua_registertotable(L, "set_lps", lua_set_rds_lps); + lua_registertotable(L, "get_lps", lua_get_rds_lps); + + lua_registertotable(L, "set_grpseq", lua_set_rds_grp_sqc); + lua_registertotable(L, "get_grpseq", lua_get_rds_grp_sqc); + + lua_registertotable(L, "set_grpseq2", lua_set_rds_grp_sqc_rds2); + lua_registertotable(L, "get_grpseq2", lua_get_rds_grp_sqc_rds2); + + lua_registertotable(L, "put_custom_group", lua_put_rds_custom_group); + lua_registertotable(L, "put_rds2_custom_group", lua_put_rds2_custom_group); + + lua_registertotable(L, "set_af", lua_set_rds_af_group0); + + lua_registertotable(L, "set_eon", lua_set_rds_eon); + lua_registertotable(L, "get_eon", lua_get_rds_eon); + + lua_registertotable(L, "set_udg", lua_set_rds_udg); + lua_registertotable(L, "set_udg2", lua_set_rds_udg2); + + lua_setglobal(L, "rds"); lua_register(L, "set_rds_streams", lua_set_rds_streams); lua_register(L, "get_rds_streams", lua_get_rds_streams); lua_register(L, "get_available_rds_streams", lua_get_available_rds_streams); - lua_register(L, "set_rds_grpseq", lua_set_rds_grp_sqc); - lua_register(L, "get_rds_grpseq", lua_get_rds_grp_sqc); - - lua_register(L, "set_rds_grpseq2", lua_set_rds_grp_sqc_rds2); - lua_register(L, "get_rds_grpseq2", lua_get_rds_grp_sqc_rds2); - - lua_register(L, "set_rds_link", lua_set_rds_link); - lua_register(L, "get_rds_link", lua_get_rds_link); - - lua_register(L, "set_rds_program", lua_set_rds_program); - lua_register(L, "get_rds_program", lua_get_rds_program); - - lua_register(L, "set_rds_rt_switching_period", lua_set_rds_rt_switching_period); - lua_register(L, "get_rds_rt_switching_period", lua_get_rds_rt_switching_period); - - lua_register(L, "set_rds_rt_text_timeout", lua_set_rds_rt_text_timeout); - lua_register(L, "get_rds_rt_text_timeout", lua_get_rds_rt_text_timeout); - lua_register(L, "set_rds_level", lua_set_rds_level); lua_register(L, "get_rds_level", lua_get_rds_level); - lua_register(L, "set_rds_ptyn", lua_set_rds_ptyn); - lua_register(L, "set_rds_ps", lua_set_rds_ps); - lua_register(L, "set_rds_tps", lua_set_rds_tps); - lua_register(L, "set_rds_rt1", lua_set_rds_rt1); - lua_register(L, "set_rds_rt2", lua_set_rds_rt2); - lua_register(L, "set_rds_default_rt", lua_set_rds_default_rt); - - lua_register(L, "set_rds_lps", lua_set_rds_lps); - lua_register(L, "get_rds_lps", lua_get_rds_lps); - - lua_register(L, "put_rds_custom_group", lua_put_rds_custom_group); - lua_register(L, "put_rds2_custom_group", lua_put_rds2_custom_group); - - lua_register(L, "set_rds_af_group0", lua_set_rds_af_group0); - - lua_register(L, "set_rds_eon", lua_set_rds_eon); - lua_register(L, "get_rds_eon", lua_get_rds_eon); - - lua_register(L, "set_rds_udg", lua_set_rds_udg); - lua_register(L, "set_rds_udg2", lua_set_rds_udg2); - - lua_register(L, "set_userdata", lua_set_userdata); - lua_register(L, "set_userdata_offset", lua_set_userdata_offset); - lua_register(L, "get_userdata", lua_get_userdata); - lua_register(L, "get_userdata_offset", lua_get_userdata_offset); - - lua_register(L, "crc16", lua_crc16); - if (luaL_loadfile(L, "/etc/rds95.lua") != LUA_OK) { fprintf(stderr, "Lua error loading file: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); @@ -562,6 +179,7 @@ void run_lua(char *str, char *cmd_output, size_t* out_len) { lua_pop(L, 1); pthread_mutex_unlock(&lua_mutex); } + int lua_group(RDSGroup* group, const char grp) { pthread_mutex_lock(&lua_mutex); lua_getglobal(L, "group"); @@ -703,7 +321,58 @@ void lua_call_function(const char* function) { pthread_mutex_unlock(&lua_mutex); } -void destroy_lua(void) { +void lua_call_table_nolock(const char *table_name) { + lua_getglobal(L, table_name); + + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return; + } + + lua_Integer len = lua_rawlen(L, -1); + for (lua_Integer i = 1; i <= len; i++) { + lua_rawgeti(L, -1, i); + if (lua_isfunction(L, -1)) { + if (lua_pcall(L, 0, 0, 0) != LUA_OK) { + fprintf(stderr, + "Lua error: %s at '%s[%lld]'\n", + lua_tostring(L, -1), + table_name, + (long long)i); + lua_pop(L, 1); + } + } else lua_pop(L, 1); + } + lua_pop(L, 1); // pop table +} +void lua_call_table(const char* function) { + int need_lock = (pthread_mutex_trylock(&lua_mutex) == 0); + if (!need_lock) { + fprintf(stderr, "Warning: lua_mutex already locked when table calling %s\n", function); + return; + } + lua_call_table_nolock(function); + pthread_mutex_unlock(&lua_mutex); +} + +void lua_call_tfunction_nolock(const char* name) { + char table_name[256]; + lua_call_function_nolock(name); + snprintf(table_name, sizeof(table_name), "%ss", name); + lua_call_table_nolock(table_name); +} + +void lua_call_tfunction(const char* name) { + int need_lock = (pthread_mutex_trylock(&lua_mutex) == 0); + if (!need_lock) { + fprintf(stderr, "Warning: lua_mutex already locked when table tcalling %s\n", name); + return; + } + lua_call_tfunction_nolock(name); + pthread_mutex_unlock(&lua_mutex); +} + +void destroy_lua() { if (L) { for (int i = 1; i < *unload_refs; i++) luaL_unref(L, LUA_REGISTRYINDEX, unload_refs[i]); *unload_refs = 1; diff --git a/src/lua_rds.h b/src/lua_rds.h index ba267f8..a174bc4 100644 --- a/src/lua_rds.h +++ b/src/lua_rds.h @@ -1,10 +1,10 @@ #pragma once +#include "common.h" +#include "rds.h" +#include "modulator.h" #include #include #include -#include "rds.h" -#include "fs.h" -#include "modulator.h" void init_lua(RDSModulator* rds_mod); void run_lua(char *str, char *cmd_output, size_t* out_len); @@ -12,5 +12,9 @@ int lua_group(RDSGroup* group, const char grp); int lua_rds2_group(RDSGroup* group, int stream); void lua_call_function_nolock(const char* function); void lua_call_function(const char* function); +void lua_call_table_nolock(const char *table_name); +void lua_call_table(const char* function); +void lua_call_tfunction_nolock(const char* name); +void lua_call_tfunction(const char* name); void lua_group_ref(RDSGroup* group, int ref); void destroy_lua(); \ No newline at end of file diff --git a/src/rds.c b/src/rds.c index cd7d51a..cd35691 100644 --- a/src/rds.c +++ b/src/rds.c @@ -155,7 +155,7 @@ void get_rds_group(RDSEncoder* enc, RDSGroup *group, uint8_t stream) { } } - lua_call_function("tick"); + lua_call_tfunction("tick"); } if (utc->tm_min != enc->state[enc->program].last_minute) { @@ -378,10 +378,10 @@ void init_rds_encoder(RDSEncoder* enc) { if (encoder_loadFromFile(enc)) { printf("Encoder file will be reinitialized.\n"); - lua_call_function("on_init"); + lua_call_tfunction("on_init"); } for(int i = 0; i < PROGRAMS; i++) reset_rds_state(enc, i); - lua_call_function("on_start"); - lua_call_function("on_state"); + lua_call_tfunction("on_start"); + lua_call_tfunction("on_state"); encoder_saveToFile(enc); } \ No newline at end of file diff --git a/src/rds95.c b/src/rds95.c index 6095b69..473f583 100644 --- a/src/rds95.c +++ b/src/rds95.c @@ -101,12 +101,12 @@ int main(int argc, char **argv) { const char *short_opt = "c:ah"; - struct option long_opt[] = + struct option long_opt[] = { - {"config", required_argument, NULL, 'c'}, - {"asciig", no_argument, NULL, 'a'}, - {"help", no_argument, NULL, 'h'}, - { 0, 0, 0, 0 } + {"config", required_argument, NULL, 'c'}, + {"asciig", no_argument, NULL, 'a'}, + {"help", no_argument, NULL, 'h'}, + {0, 0, 0, 0} }; int opt; @@ -144,7 +144,6 @@ int main(int argc, char **argv) { pthread_attr_init(&attr); struct sigaction sa_stop; - sa_stop.sa_handler = stop; sigemptyset(&sa_stop.sa_mask); sa_stop.sa_flags = 0; @@ -160,17 +159,7 @@ int main(int argc, char **argv) { buffer.tlength = buffer.maxlength = NUM_MPX_FRAMES * config.num_streams; if(config.asciig == 0) { - rds_device = pa_simple_new( - NULL, - "rds95", - PA_STREAM_PLAYBACK, - config.rds_device_name, - "RDS Generator", - &format, - NULL, - &buffer, - NULL - ); + rds_device = pa_simple_new(NULL, "rds95", PA_STREAM_PLAYBACK, config.rds_device_name, "RDS Generator", &format, NULL, &buffer, NULL); if (rds_device == NULL) { fprintf(stderr, "Error: cannot open sound device.\n"); goto exit;