Skip to content

Commit 36c4ef3

Browse files
committed
library enigmaxml extractor function working
1 parent faf7682 commit 36c4ef3

File tree

2 files changed

+87
-37
lines changed

2 files changed

+87
-37
lines changed

src/library/enigmaxml.lua

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
--[[
2+
$module Functions for accessing Enigma XML files.
3+
4+
Enigma XML is the underlying file format of a Finale `.musx` file. It is undocumented
5+
by MakeMusic and must be extracted from the `.musx` file. There is an effort to document
6+
it underway at the [EnigmaXML Documentation](https://github.com/finale-lua/enigmaxml-documentation)
7+
repository.
8+
]] --
9+
local enigmaxml = {}
10+
11+
local utils = require("library.utils")
12+
13+
-- symmetrical encryption/decryption function
14+
local function crypt_enigmaxml_buffer(buffer)
15+
local state = 0x28006D45 -- this value was determined empirically
16+
local result = {}
17+
18+
for i = 1, #buffer do
19+
-- BSD rand()
20+
state = (state * 0x41c64e6d + 0x3039) & 0xFFFFFFFF -- Simulate 32-bit overflow
21+
local upper = state >> 16
22+
local c = upper + math.floor(upper / 255)
23+
24+
local byte = string.byte(buffer, i)
25+
byte = byte ~ (c & 0xFF) -- XOR operation on the byte
26+
27+
table.insert(result, string.char(byte))
28+
end
29+
30+
return table.concat(result)
31+
end
32+
33+
--[[
34+
%extract_xml
35+
36+
Extracts an enigmaxml buffer from a `.musx` file. Note that this function does not work with Finale's
37+
older `.mus` format.
38+
39+
Windows users must have `7z` installed and macOS users must have `unzip`.
40+
41+
@ filepath (string) a file path to a `.musx` file.
42+
: (string) buffer of xml data containing the EnigmaXml extracted from the `.musx`
43+
]]
44+
function enigmaxml.extract_xml(filepath)
45+
if finenv.MajorVersion <= 0 and finenv.MinorVersion < 68 then
46+
error("enigmaxml.extract_xml requires at least RGP Lua v0.68.", 2)
47+
end
48+
if finenv.TrustedMode == finenv.TrustedModeType.UNTRUSTED then
49+
error("enigmaxml.extract_xml must run in Trusted mode.", 2)
50+
end
51+
local _, _, extension = utils.split_file_path(filepath)
52+
if extension ~= ".musx" then
53+
error(filepath .. " is not a .musx file.", 2)
54+
end
55+
56+
local text = require("luaosutils").text
57+
local process = require("luaosutils").process
58+
59+
local os_filepath = text.convert_encoding(filepath, text.get_utf8_codepage(), text.get_default_codepage())
60+
local output_dir = os.tmpname()
61+
local rmcommand = (finenv.UI():IsOnMac() and "rm " or "cmd /c del ") .. output_dir
62+
process.execute(rmcommand)
63+
64+
local zipcommand
65+
if finenv.UI():IsOnMac() then
66+
zipcommand = "unzip \"" .. os_filepath .. "\" -d " .. output_dir
67+
else
68+
zipcommand = "cmd /c 7z x -o" .. output_dir .. " \"" .. os_filepath .. "\""
69+
end
70+
if not process.execute(zipcommand) then
71+
error(zipcommand .. " failed")
72+
end
73+
74+
local file <close> = io.open(output_dir .. "/score.dat", "rb")
75+
if not file then
76+
error("unable to read " .. output_dir .. "/score.dat")
77+
end
78+
local buffer = file:read("*all")
79+
file:close()
80+
81+
local delcommand = (finenv.UI():IsOnMac() and "rm -r " or "cmd /c rmdir /s /q ") .. output_dir
82+
process.execute(delcommand)
83+
84+
return crypt_enigmaxml_buffer(buffer)
85+
end
86+
87+
return enigmaxml

src/library/general_library.lua

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -632,41 +632,4 @@ function library.get_default_music_font_name()
632632
end
633633
end
634634

635-
--[[
636-
%crypt_musx_file_buffer
637-
638-
Symmetrical encryption/decryption for `.musx` files. Finale `.musx` files are byte-for-byte encrypted
639-
gzip archives. The archives contain xml and other data for the Finale document. This function accepts
640-
either a buffer of encrypted text and returns plaintext or accepts plaintext and returns encrypted text.
641-
642-
To decrypt a `.musx`,
643-
644-
- read the `.musx` file into a buffer using binary read mode
645-
- pass it to this function
646-
- save the return buffer as `.gz` using binary write mode
647-
648-
Reverse the process to create a `.musx` from a `.gz` file.
649-
650-
@ buffer (string) buffer of binary data containing plaintext or encrypted text of a `.musx` file.
651-
: (string) buffer of binary data containing encrypted text or plaintext corresponding to the input buffer.
652-
]]
653-
function library.crypt_musx_file_buffer(buffer)
654-
local state = 0x28006D45 -- this value was determined empirically
655-
local result = {}
656-
657-
for i = 1, #buffer do
658-
-- BSD rand()
659-
state = (state * 0x41c64e6d + 0x3039) & 0xFFFFFFFF -- Simulate 32-bit overflow
660-
local upper = state >> 16
661-
local c = upper + math.floor(upper / 255)
662-
663-
local byte = string.byte(buffer, i)
664-
byte = byte ~ (c & 0xFF) -- XOR operation on the byte
665-
666-
table.insert(result, string.char(byte))
667-
end
668-
669-
return table.concat(result)
670-
end
671-
672635
return library

0 commit comments

Comments
 (0)