/*
 *  compression/ZipExtractor.cpp
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Milkytracker is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 *  ZipExtractor.cpp
 *  milkytracker_universal
 *
 *  Created by Peter Barth on 31.01.07.
 *
 */

#ifdef VERBOSE
#include <stdio.h>
#endif
#include "ZipExtractor.h"
#include "XModule.h"
#include "XMFile.h"
#include "MyIO.h"
#include <zzip/lib.h>

static const struct zzip_plugin_io milkytracker_zzip_io =
{
    &Myopen,
    &Myclose,
    &Myread,
    &Mylseek,
    &Myfsize,
    1, 1,
    NULL
};

ZipExtractor::ZipExtractor(const PPSystemString& archivePath) :
	archivePath(archivePath)
{
}

bool ZipExtractor::parseZip(pp_int32& err, bool extract, const PPSystemString* outFile)
{
    int i;
	int fd;
    
	ZZIP_DIR * dir;
    zzip_error_t rv;
  
#ifdef VERBOSE
    printf("Opening zip file `%s'... ", (const char*)archivePath);
#endif
    { 	
		const SYSCHAR* fileName = archivePath;
		fd = Myopen((const zzip_char_t*)fileName, O_RDONLY);
        
		if (fd == -1)
		{ 
			err = 1;
			return false;
		}
        
		if (! (dir = zzip_dir_fdopen_ext_io(fd, &rv, NULL, (zzip_plugin_io_t) &milkytracker_zzip_io)))
        {
			err = 2;
			return false;
        }
    } 
#ifdef VERBOSE
	printf("OK.\n");  
    printf("{check...\n");
#endif
    { 
		struct zzip_dir_hdr * hdr = dir->hdr0;
    
        if (hdr == NULL) 
        { 
#ifdef VERBOSE
			printf ("could not find first header in dir_hdr"); 
#endif
			err = 3;
			zzip_dir_close(dir);
			return false;
		}
        else
        {   
            while (1)
            {
#ifdef VERBOSE
                printf("\ncompression method: %d", hdr->d_compr);
                if (hdr->d_compr == 0) printf(" (stored)");
                else if (hdr->d_compr == 8) printf(" (deflated)");
                else printf(" (unknown)");
                printf("\ncrc32: %x\n", hdr->d_crc32);
                printf("compressed size: %d\n", hdr->d_csize);
                printf("uncompressed size: %d\n", hdr->d_usize);
                printf("offset of file in archive: %d\n", hdr->d_off);
                printf("filename: %s\n\n", hdr->d_name);
#endif    
				{   
					ZZIP_FILE *fp;
					mp_ubyte* buf = new mp_ubyte[16384];

					if (buf == NULL)
					{
						err = 6;
						zzip_dir_close(dir);
						return false;
					}

					memset(buf, 0, 16384);

					const char *name = hdr->d_name;
					
#ifdef VERBOSE
					printf("Opening file `%s' in zip archive... ", name);    
#endif
					fp = zzip_file_open(dir, (char *)name, ZZIP_CASEINSENSITIVE);
					
					if (! fp)
					{ 
#ifdef VERBOSE
						printf("error %d: %s\n", zzip_error(dir), zzip_strerror_of(dir)); 
#endif
						err = 7;
						delete[] buf;
						zzip_dir_close(dir);
						return false;
					}
					else
					{
#ifdef VERBOSE
						printf("OK.\n");
						printf("Contents of the file:\n");
#endif						
						i = zzip_file_read(fp, (char*)buf, 16384);
						
						const char* id = XModule::identifyModule(buf);

						if (id)
						{														
							if (extract)
							{
								XMFile f(*outFile, true);
								f.write(buf, 1, i);
								while (0 < (i = zzip_file_read(fp, (char*)buf, 16384)))
								{
									f.write(buf, 1, i);
								}
								if (i < 0)
								{
#ifdef VERBOSE
									printf("error %d\n", zzip_error(dir));
#endif
									err = 4;
									delete[] buf;
									
									zzip_file_close(fp);							
									zzip_dir_close(dir);									
									return false;
								}								
							}

							delete[] buf;
							
							err = 0;
							
							zzip_file_close(fp);							
							zzip_dir_close(dir);							
							return true;

						}

						zzip_file_close(fp);

					}
					
					delete[] buf;
				}  

                if (hdr->d_reclen == 0) break;
            
				char* temp = (char*)hdr;
				temp+=hdr->d_reclen;
				hdr = (zzip_dir_hdr*)temp;
				
				//(char *)hdr += hdr->d_reclen;
            }
        }
    } 
#ifdef VERBOSE
	printf ("\n}\n");
#endif
	zzip_dir_close(dir);	
	err = 5;
	return false;
}
