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