1 /* __license__ = 'GPL v3'
2 * __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
3 *
4 * Python module C glue code.
5 */
6 #define PY_SSIZE_T_CLEAN
7
8 #include <Python.h>
9
10 #include <mspack.h>
11 #include <lzxd.h>
12
13 extern PyObject *LZXError;
14 extern PyTypeObject CompressorType;
15
16 static char lzx_doc[] =
17 "Provide basic LZX compression and decompression using the code from\n"
18 "liblzxcomp and libmspack respectively.";
19
20 PyObject *LZXError = NULL;
21
22 typedef struct memory_file {
23 unsigned int magic; /* 0xB5 */
24 void *buffer;
25 int total_bytes;
26 int current_bytes;
27 } memory_file;
28
29 static void *
glue_alloc(struct mspack_system * this,size_t bytes)30 glue_alloc(struct mspack_system *this, size_t bytes)
31 {
32 void *p = NULL;
33 p = (void *)malloc(bytes);
34 if (p == NULL) {
35 return (void *)PyErr_NoMemory();
36 }
37 return p;
38 }
39
40 static void
glue_free(void * p)41 glue_free(void *p)
42 {
43 free(p);
44 }
45
46 static void
glue_copy(void * src,void * dest,size_t bytes)47 glue_copy(void *src, void *dest, size_t bytes)
48 {
49 memcpy(dest, src, bytes);
50 }
51
52 static struct mspack_file *
glue_open(struct mspack_system * this,char * filename,int mode)53 glue_open(struct mspack_system *this, char *filename, int mode)
54 {
55 PyErr_SetString(LZXError, "MSPACK_OPEN unsupported");
56 return NULL;
57 }
58
59 static void
glue_close(struct mspack_file * file)60 glue_close(struct mspack_file *file)
61 {
62 return;
63 }
64
65 static int
glue_read(struct mspack_file * file,void * buffer,int bytes)66 glue_read(struct mspack_file *file, void *buffer, int bytes)
67 {
68 memory_file *mem;
69 int remaining;
70
71 mem = (memory_file *)file;
72 if (mem->magic != 0xB5) return -1;
73
74 remaining = mem->total_bytes - mem->current_bytes;
75 if (!remaining) return 0;
76 if (bytes > remaining) bytes = remaining;
77 memcpy(buffer, (unsigned char *)mem->buffer + mem->current_bytes, bytes);
78 mem->current_bytes += bytes;
79
80 return bytes;
81 }
82
83 static int
glue_write(struct mspack_file * file,void * buffer,int bytes)84 glue_write(struct mspack_file *file, void *buffer, int bytes)
85 {
86 memory_file *mem;
87 int remaining;
88
89 mem = (memory_file *)file;
90 if (mem->magic != 0xB5) return -1;
91
92 remaining = mem->total_bytes - mem->current_bytes;
93 if (bytes > remaining) {
94 PyErr_SetString(LZXError,
95 "MSPACK_WRITE tried to write beyond end of buffer");
96 bytes = remaining;
97 }
98 memcpy((unsigned char *)mem->buffer + mem->current_bytes, buffer, bytes);
99 mem->current_bytes += bytes;
100 return bytes;
101 }
102
103 struct mspack_system lzxglue_system = {
104 glue_open,
105 glue_close,
106 glue_read, /* Read */
107 glue_write, /* Write */
108 NULL, /* Seek */
109 NULL, /* Tell */
110 NULL, /* Message */
111 glue_alloc,
112 glue_free,
113 glue_copy,
114 NULL /* Termination */
115 };
116
117
118 int LZXwindow = 0;
119 struct lzxd_stream * lzx_stream = NULL;
120
121 /* Can't really init here, don't know enough */
122 static PyObject *
init(PyObject * self,PyObject * args)123 init(PyObject *self, PyObject *args)
124 {
125 int window = 0;
126
127 if (!PyArg_ParseTuple(args, "i", &window)) {
128 return NULL;
129 }
130
131 LZXwindow = window;
132 lzx_stream = NULL;
133
134 Py_RETURN_NONE;
135 }
136
137 /* Doesn't exist. Oh well, reinitialize state every time anyway */
138 static PyObject *
reset(PyObject * self,PyObject * args)139 reset(PyObject *self, PyObject *args)
140 {
141 if (!PyArg_ParseTuple(args, "")) {
142 return NULL;
143 }
144
145 Py_RETURN_NONE;
146 }
147
148 //int LZXdecompress(unsigned char *inbuf, unsigned char *outbuf,
149 // unsigned int inlen, unsigned int outlen)
150 static PyObject *
decompress(PyObject * self,PyObject * args)151 decompress(PyObject *self, PyObject *args)
152 {
153 unsigned char *inbuf;
154 unsigned char *outbuf;
155 Py_ssize_t inlen;
156 unsigned int outlen;
157 int err;
158 memory_file source;
159 memory_file dest;
160 PyObject *retval = NULL;
161
162 if (!PyArg_ParseTuple(args, "y#I", &inbuf, &inlen, &outlen)) {
163 return NULL;
164 }
165
166 retval = PyBytes_FromStringAndSize(NULL, outlen);
167 if (retval == NULL) {
168 return NULL;
169 }
170 outbuf = (unsigned char *)PyBytes_AS_STRING(retval);
171
172 source.magic = 0xB5;
173 source.buffer = inbuf;
174 source.current_bytes = 0;
175 source.total_bytes = inlen;
176
177 dest.magic = 0xB5;
178 dest.buffer = outbuf;
179 dest.current_bytes = 0;
180 dest.total_bytes = outlen;
181
182 lzx_stream = lzxd_init(&lzxglue_system, (struct mspack_file *)&source,
183 (struct mspack_file *)&dest, LZXwindow,
184 0x7fff /* Never reset, I do it */, 4096, outlen);
185 err = -1;
186 if (lzx_stream) err = lzxd_decompress(lzx_stream, outlen);
187
188 lzxd_free(lzx_stream);
189 lzx_stream = NULL;
190
191 if (err != MSPACK_ERR_OK) {
192 Py_DECREF(retval);
193 retval = NULL;
194 PyErr_SetString(LZXError, "LZX decompression failed");
195 }
196
197 return retval;
198 }
199
200 static PyMethodDef lzx_methods[] = {
201 { "init", &init, METH_VARARGS, "Initialize the LZX decompressor" },
202 { "reset", &reset, METH_VARARGS, "Reset the LZX decompressor" },
203 { "decompress", &decompress, METH_VARARGS, "Run the LZX decompressor" },
204 { NULL }
205 };
206
207 static int
exec_module(PyObject * m)208 exec_module(PyObject *m) {
209 if (PyType_Ready(&CompressorType) < 0) return -1;
210
211 LZXError = PyErr_NewException("lzx.LZXError", NULL, NULL);
212 Py_INCREF(LZXError);
213 PyModule_AddObject(m, "LZXError", LZXError);
214
215 Py_INCREF(&CompressorType);
216 PyModule_AddObject(m, "Compressor", (PyObject *)&CompressorType);
217
218 return 0;
219 }
220
221 static PyModuleDef_Slot slots[] = { {Py_mod_exec, exec_module}, {0, NULL} };
222
223 static struct PyModuleDef module_def = {
224 .m_base = PyModuleDef_HEAD_INIT,
225 .m_name = "lzx",
226 .m_doc = lzx_doc,
227 .m_methods = lzx_methods,
228 .m_slots = slots,
229 };
230
PyInit_lzx(void)231 CALIBRE_MODINIT_FUNC PyInit_lzx(void) { return PyModuleDef_Init(&module_def); }
232