sq_zlib.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. #include <ctype.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <zlib.h>
  6. #include <unzip.h>
  7. #include "squirrel.h"
  8. #include "sqstdblobimpl.h"
  9. SQ_OPT_STRING_STRLEN();
  10. #define LZ_BUFFER_SIZE 8192
  11. #define LZ_MAX_ZIP_NAME_SIZE 256
  12. #define ZIP_DOS_DIR_ATTRIBUTE_BITFLAG 0x10
  13. static const SQChar sq_minizip_unzip_TAG[] = _SC("sq_minizip_unzip_tag");
  14. #define GET_minizip_unzip_INSTANCE_VAR_AT(idx, Var) \
  15. SQ_GET_INSTANCE_VAR(v, idx, unzFile, Var, sq_minizip_unzip_TAG);\
  16. if(!Var) return sq_throwerror(v, _SC("miniz_ziparchive already destroyed"));
  17. #define GET_minizip_unzip_INSTANCE() GET_minizip_unzip_INSTANCE_VAR_AT(1, self)
  18. static SQRESULT sq_minizip_unzip_releasehook(SQUserPointer p, SQInteger size, void */*ep*/)
  19. {
  20. unzFile self = ((unzFile)p);
  21. if(self) {
  22. unzClose(self);
  23. }
  24. return 0;
  25. }
  26. static SQRESULT sq_minizip_unzip_constructor(HSQUIRRELVM v)
  27. {
  28. SQ_FUNC_VARS_NO_TOP(v);
  29. SQ_GET_STRING(v, 2, zip_fname);
  30. unzFile zip_archive = unzOpen64(zip_fname);
  31. if(!zip_archive)
  32. {
  33. return sq_throwerror(v, _SC("Cannot open %s\n"),zip_fname);
  34. }
  35. sq_setinstanceup(v, 1, zip_archive);
  36. sq_setreleasehook(v,1, sq_minizip_unzip_releasehook);
  37. return 1;
  38. }
  39. static SQRESULT sq_minizip_unzip_get_num_files(HSQUIRRELVM v)
  40. {
  41. SQ_FUNC_VARS_NO_TOP(v);
  42. GET_minizip_unzip_INSTANCE();
  43. unz_global_info64 gi;
  44. int err = unzGetGlobalInfo64(self,&gi);
  45. if(err != UNZ_OK)
  46. {
  47. return sq_throwerror(v, _SC("error %d with zipfile in unzGetGlobalInfo \n"), err);
  48. }
  49. sq_pushinteger(v, gi.number_entry);
  50. return 1;
  51. }
  52. static SQRESULT sq_minizip_unzip_locate_file(HSQUIRRELVM v)
  53. {
  54. SQ_FUNC_VARS(v);
  55. GET_minizip_unzip_INSTANCE();
  56. SQ_GET_STRING(v, 2, fname);
  57. SQ_OPT_BOOL(v, 3, iCaseSensitivity, false);
  58. sq_pushbool(v, unzLocateFile(self, fname, iCaseSensitivity) == UNZ_OK);
  59. return 1;
  60. }
  61. static SQRESULT sq_minizip_unzip_goto_first_file(HSQUIRRELVM v)
  62. {
  63. SQ_FUNC_VARS_NO_TOP(v);
  64. GET_minizip_unzip_INSTANCE();
  65. sq_pushbool(v, unzGoToFirstFile(self) == UNZ_OK);
  66. return 1;
  67. }
  68. static SQRESULT sq_minizip_unzip_goto_next_file(HSQUIRRELVM v)
  69. {
  70. SQ_FUNC_VARS_NO_TOP(v);
  71. GET_minizip_unzip_INSTANCE();
  72. sq_pushbool(v, unzGoToNextFile(self) == UNZ_OK);
  73. return 1;
  74. }
  75. static SQRESULT sq_minizip_unzip_get_file_info(HSQUIRRELVM v)
  76. {
  77. SQ_FUNC_VARS_NO_TOP(v);
  78. GET_minizip_unzip_INSTANCE();
  79. char filename_inzip[LZ_MAX_ZIP_NAME_SIZE];
  80. unz_file_info64 file_info;
  81. int err = unzGetCurrentFileInfo64(self,&file_info,filename_inzip,LZ_MAX_ZIP_NAME_SIZE,NULL,0,NULL,0);
  82. if (err!=UNZ_OK)
  83. {
  84. return sq_throwerror(v, _SC("error %d with zipfile in unzGetCurrentFileInfo\n"),err);
  85. }
  86. sq_newtable(v);
  87. sq_pushliteral(v, "name");
  88. sq_pushstring(v, filename_inzip, -1);
  89. sq_rawset(v, -3);
  90. sq_pushliteral(v, "size");
  91. sq_pushinteger(v, file_info.uncompressed_size);
  92. sq_rawset(v, -3);
  93. sq_pushliteral(v, "is_dir");
  94. sq_pushbool(v, file_info.external_fa & ZIP_DOS_DIR_ATTRIBUTE_BITFLAG);
  95. sq_rawset(v, -3);
  96. return 1;
  97. }
  98. static SQRESULT sq_minizip_unzip_extract_file(HSQUIRRELVM v)
  99. {
  100. SQ_FUNC_VARS_NO_TOP(v);
  101. GET_minizip_unzip_INSTANCE();
  102. int err = unzOpenCurrentFile(self);
  103. if (err!=UNZ_OK)
  104. {
  105. return sq_throwerror(v, _SC("error %d with zipfile in unzGetCurrentFileInfo\n"), err);
  106. }
  107. SQBlob b(0, 8192);
  108. char buf[8192];
  109. uInt size_buf = sizeof(buf);
  110. do
  111. {
  112. err = unzReadCurrentFile(self,buf,size_buf);
  113. if (err<0)
  114. {
  115. return sq_throwerror(v, _SC("error %d with zipfile in unzReadCurrentFile\n"), err);
  116. }
  117. if (err>0)
  118. {
  119. b.Write(buf, err);
  120. }
  121. }
  122. while (err>0);
  123. err = unzCloseCurrentFile(self);
  124. sq_pushstring(v, (SQChar*)b.GetBuf(), b.Len());
  125. return 1;
  126. }
  127. static SQRESULT sq_minizip_unzip_destroy(HSQUIRRELVM v)
  128. {
  129. SQ_FUNC_VARS_NO_TOP(v);
  130. GET_minizip_unzip_INSTANCE();
  131. int rc = sq_minizip_unzip_releasehook(self, 0, v);
  132. if(rc == 0) sq_setinstanceup(v, 1, 0);
  133. sq_pushinteger(v, rc);
  134. return 1;
  135. }
  136. #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_minizip_unzip_##name,nparams,tycheck}
  137. static SQRegFunction sq_minizip_unzip_methods[] =
  138. {
  139. _DECL_FUNC(constructor, 2, _SC("xs")),
  140. _DECL_FUNC(get_num_files, 1, _SC("x")),
  141. _DECL_FUNC(locate_file, -2, _SC("xsb")),
  142. _DECL_FUNC(goto_first_file, 1, _SC("x")),
  143. _DECL_FUNC(goto_next_file, 1, _SC("x")),
  144. _DECL_FUNC(get_file_info, 1, _SC("x")),
  145. _DECL_FUNC(extract_file, 1, _SC("x")),
  146. _DECL_FUNC(destroy, 1, _SC("x")),
  147. {0,0}
  148. };
  149. #undef _DECL_FUNC
  150. #if 0
  151. static const SQChar sq_minizip_unzip_TAG[] = _SC("sq_minizip_zip_tag");
  152. #define GET_minizip_zip_INSTANCE_VAR_AT(idx, Var) \
  153. SQ_GET_INSTANCE_VAR(v, idx, zipFile, Var, sq_minizip_zip_TAG);\
  154. if(!Var) return sq_throwerror(v, _SC("miniz_ziparchive already destroyed"));
  155. #define GET_minizip_zip_INSTANCE() GET_minizip_zip_INSTANCE_VAR_AT(1, self)
  156. static SQRESULT sq_minizip_zip_releasehook(SQUserPointer p, SQInteger size, void */*ep*/)
  157. {
  158. zipFile self = ((zipFile)p);
  159. if(self) {
  160. zipClose(self);
  161. }
  162. return 0;
  163. }
  164. static SQRESULT sq_minizip_zip_constructor(HSQUIRRELVM v)
  165. {
  166. SQ_FUNC_VARS_NO_TOP(v);
  167. SQ_GET_STRING(v, 2, zip_fname);
  168. SQ_GET_INTEGER(v, 3, append);
  169. zipFile zip_archive = zipOpen64(zip_fname, append);
  170. if(!zip_archive)
  171. {
  172. return sq_throwerror(v, _SC("Cannot open %s\n"),zip_fname);
  173. }
  174. sq_setinstanceup(v, 1, zip_archive);
  175. sq_setreleasehook(v,1, sq_minizip_zip_releasehook);
  176. return 1;
  177. }
  178. static SQRESULT sq_minizip_zip_destroy(HSQUIRRELVM v)
  179. {
  180. SQ_FUNC_VARS_NO_TOP(v);
  181. GET_minizip_zip_INSTANCE();
  182. int rc = sq_minizip_zip_releasehook(self, 0, v);
  183. if(rc == 0) sq_setinstanceup(v, 1, 0);
  184. sq_pushinteger(v, rc);
  185. return 1;
  186. }
  187. #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_minizip_zip_##name,nparams,tycheck}
  188. static SQRegFunction sq_minizip_zip_methods[] =
  189. {
  190. _DECL_FUNC(constructor, 3, _SC("xsi")),
  191. _DECL_FUNC(new_file, 3, _SC("xss")),
  192. _DECL_FUNC(destroy, 1, _SC("x")),
  193. {0,0}
  194. };
  195. #undef _DECL_FUNC
  196. #endif
  197. static SQRESULT sq_check_result(HSQUIRRELVM v, int result, const z_stream* stream) {
  198. /* Both of these are "normal" return codes: */
  199. if ( result == Z_OK || result == Z_STREAM_END ) return SQ_OK;
  200. switch ( result ) {
  201. case Z_NEED_DICT:
  202. return sq_throwerror(v, _SC("RequiresDictionary: input stream requires a dictionary to be deflated (%s)"),
  203. stream->msg);
  204. break;
  205. case Z_STREAM_ERROR:
  206. return sq_throwerror(v, _SC("InternalError: inconsistent internal zlib stream (%s)"),
  207. stream->msg);
  208. break;
  209. case Z_DATA_ERROR:
  210. return sq_throwerror(v, _SC("InvalidInput: input string does not conform to zlib format or checksum"));
  211. break;
  212. case Z_MEM_ERROR:
  213. return sq_throwerror(v, _SC("OutOfMemory: not enough memory (%s)"), stream->msg);
  214. break;
  215. case Z_BUF_ERROR:
  216. return sq_throwerror(v, _SC("InternalError: no progress possible (%s)"), stream->msg);
  217. break;
  218. case Z_VERSION_ERROR:
  219. return sq_throwerror(v, _SC("IncompatibleLibrary: built with version %s, but dynamically linked with version %s (%s)"),
  220. ZLIB_VERSION, zlibVersion(), stream->msg);
  221. break;
  222. default:
  223. return sq_throwerror(v, _SC("ZLibError: unknown code %d (%s)"), result, stream->msg);
  224. }
  225. return SQ_OK;
  226. }
  227. static SQRESULT sq_zlib_deflate(HSQUIRRELVM v)
  228. {
  229. SQ_FUNC_VARS(v);
  230. SQ_GET_STRING(v, 2, data);
  231. SQ_OPT_INTEGER(v, 3, level, Z_DEFAULT_COMPRESSION);
  232. /* Allocate the stream: */
  233. z_stream stream;
  234. stream.zalloc = Z_NULL;
  235. stream.zfree = Z_NULL;
  236. if(sq_check_result(v, deflateInit(&stream, level), &stream) != SQ_OK) return SQ_ERROR;
  237. SQBlob b(LZ_BUFFER_SIZE);
  238. /* Do the actual deflate'ing: */
  239. stream.next_in = (unsigned char*)data;
  240. stream.avail_in = data_size;
  241. if ( ! stream.avail_in ) {
  242. /* Passed empty string, make it a noop instead of erroring out. */
  243. sq_pushliteral(v, "");
  244. return 1;
  245. }
  246. int nchunks = 0;
  247. do {
  248. stream.next_out = ((unsigned char*)b.GetBuf()) + (LZ_BUFFER_SIZE * nchunks++);
  249. stream.avail_out = LZ_BUFFER_SIZE;
  250. int result = deflate(&stream, Z_FINISH);
  251. if (result == Z_STREAM_END )
  252. break;
  253. if (result != Z_OK)
  254. {
  255. sq_check_result(v, result, &stream);
  256. deflateEnd(&stream);
  257. return SQ_ERROR;
  258. }
  259. b.GrowBufOf(LZ_BUFFER_SIZE);
  260. } while ( stream.avail_out == 0 );
  261. SQInteger total_out = stream.total_out;
  262. /* Close the stream: */
  263. if(sq_check_result(v, deflateEnd(&stream), &stream) == SQ_ERROR) return SQ_ERROR;
  264. /* Need to do this before we alter the stack: */
  265. sq_pushstring(v, (const SQChar*)b.GetBuf(), total_out);
  266. return 1;
  267. }
  268. static SQRESULT sq_zlib_inflate(HSQUIRRELVM v)
  269. {
  270. SQ_FUNC_VARS(v);
  271. SQ_GET_STRING(v, 2, data);
  272. /* By default, we will do gzip header detection w/ max window size */
  273. SQ_OPT_INTEGER(v, 3, window_size, MAX_WBITS + 32);
  274. /* Allocate the stream: */
  275. z_stream stream;
  276. stream.zalloc = Z_NULL;
  277. stream.zfree = Z_NULL;
  278. if(sq_check_result(v, inflateInit2(&stream, window_size), &stream) != SQ_OK) return SQ_ERROR;
  279. SQBlob b(LZ_BUFFER_SIZE);
  280. /* Do the actual deflate'ing: */
  281. stream.next_in = (unsigned char*)data;
  282. stream.avail_in = data_size;
  283. if ( ! stream.avail_in ) {
  284. /* Passed empty string, make it a noop instead of erroring out. */
  285. sq_pushliteral(v, "");
  286. return 1;
  287. }
  288. int nchunks = 0;
  289. do {
  290. stream.next_out = ((unsigned char*)b.GetBuf()) + (LZ_BUFFER_SIZE * nchunks++);
  291. stream.avail_out = LZ_BUFFER_SIZE;
  292. int result = inflate(&stream, Z_FINISH);
  293. if ( Z_BUF_ERROR != result ) {
  294. /* Ignore Z_BUF_ERROR since that just indicates that we
  295. * need a larger buffer in order to proceed. Thanks to
  296. * Tobias Markmann for finding this bug!
  297. */
  298. if(sq_check_result(v, result, &stream) == SQ_OK) break;
  299. else {
  300. inflateEnd(&stream);
  301. return SQ_ERROR;
  302. }
  303. }
  304. b.GrowBufOf(LZ_BUFFER_SIZE);
  305. } while ( stream.avail_out == 0 );
  306. SQInteger total_out = stream.total_out;
  307. /* Close the stream: */
  308. if(sq_check_result(v, inflateEnd(&stream), &stream) == SQ_ERROR) return SQ_ERROR;
  309. /* Need to do this before we alter the stack: */
  310. sq_pushstring(v, (const SQChar*)b.GetBuf(), total_out);
  311. return 1;
  312. }
  313. static SQRESULT sq_zlib_version(HSQUIRRELVM v)
  314. {
  315. sq_pushstring(v, zlibVersion(), -1);
  316. return 1;
  317. }
  318. #ifdef __cplusplus
  319. extern "C" {
  320. #endif
  321. #define INT_CONST(v,num) sq_pushstring(v,_SC(#num),-1);sq_pushinteger(v,num);sq_newslot(v,-3,SQTrue);
  322. SQRESULT sqext_register_sq_zlib(HSQUIRRELVM v)
  323. {
  324. sq_pushliteral(v,_SC("zlib"));
  325. sq_newtable(v);
  326. INT_CONST(v, Z_DEFAULT_COMPRESSION);
  327. INT_CONST(v, Z_BEST_SPEED);
  328. INT_CONST(v, Z_BEST_COMPRESSION);
  329. sq_insertfunc(v, _SC("version"), sq_zlib_version, 1, _SC("."), SQTrue);
  330. sq_insertfunc(v, _SC("deflate"), sq_zlib_deflate, -2, _SC(".si"), SQTrue);
  331. sq_insertfunc(v, _SC("inflate"), sq_zlib_inflate, -2, _SC(".si"), SQTrue);
  332. sq_pushliteral(v,_SC("Unzip"));
  333. sq_newclass(v,SQFalse);
  334. sq_settypetag(v,-1,(void*)sq_minizip_unzip_TAG);
  335. sq_insert_reg_funcs(v, sq_minizip_unzip_methods);
  336. sq_newslot(v,-3,SQTrue);
  337. sq_newslot(v,-3,SQTrue); //push zlib table
  338. return 0;
  339. }
  340. #ifdef __cplusplus
  341. }
  342. #endif