1 /*
2  *  compression/DecompressorLHA.cpp
3  *
4  *  Copyright 2009 Peter Barth
5  *
6  *  This file is part of Milkytracker.
7  *
8  *  Milkytracker is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Milkytracker is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 /*
24  *  DecompressorLHA.cpp
25  *  milkytracker_universal
26  *
27  *  Created by Peter Barth on 22.06.08.
28  *
29  */
30 
31 #include <lhasa.h>
32 
33 #include "DecompressorLHA.h"
34 #include "XMFile.h"
35 #include "XModule.h"
36 
37 #define LHA_BUFFER_SIZE 0x10000
38 
39 namespace
40 {
lha_read_callback(void * handle,void * buf,size_t buf_len)41 	static int lha_read_callback(void *handle, void *buf, size_t buf_len)
42 	{
43 		return static_cast<XMFile*>(handle)->read(buf, 1, buf_len);
44 	}
45 
46 	static const LHAInputStreamType lha_callbacks =
47 	{
48 		lha_read_callback,
49 		NULL,
50 		NULL
51 	};
52 
53 	// Simple wrapper for lha_reader_*
54 	class LHAReaderWrapper
55 	{
56 	public:
LHAReaderWrapper(XMFile & file)57 		explicit LHAReaderWrapper(XMFile& file) : input_stream(NULL), reader(NULL)
58 		{
59 			// Open input stream
60 			input_stream = lha_input_stream_new(&lha_callbacks, &file);
61 			if (input_stream == NULL)
62 				return;
63 
64 			// Open reader
65 			reader = lha_reader_new(input_stream);
66 		}
67 
isOpen() const68 		bool isOpen() const
69 		{
70 			return reader != NULL;
71 		}
72 
nextFile()73 		LHAFileHeader* nextFile()
74 		{
75 			return lha_reader_next_file(reader);
76 		}
77 
read(void * buf,size_t length)78 		size_t read(void* buf, size_t length)
79 		{
80 			return lha_reader_read(reader, buf, length);
81 		}
82 
~LHAReaderWrapper()83 		~LHAReaderWrapper()
84 		{
85 			if (reader != NULL)
86 				lha_reader_free(reader);
87 			if (input_stream != NULL)
88 				lha_input_stream_free(input_stream);
89 		}
90 
91 	private:
92 		LHAInputStream* input_stream;
93 		LHAReader* reader;
94 
95 		LHAReaderWrapper(const LHAReaderWrapper&);
96 		LHAReaderWrapper& operator=(const LHAReaderWrapper&);
97 	};
98 }
99 
DecompressorLHA(const PPSystemString & filename)100 DecompressorLHA::DecompressorLHA(const PPSystemString& filename) :
101 	DecompressorBase(filename)
102 {
103 }
104 
identify(XMFile & f)105 bool DecompressorLHA::identify(XMFile& f)
106 {
107 	f.seek(0);
108 
109 	// Attempt to create the reader and read the header of the first file
110 	LHAReaderWrapper reader(f);
111 	if (!reader.isOpen())
112 		return false;
113 
114 	return reader.nextFile() != NULL;
115 }
116 
getDescriptors(Hints hint) const117 const PPSimpleVector<Descriptor>& DecompressorLHA::getDescriptors(Hints hint) const
118 {
119 	descriptors.clear();
120 	descriptors.add(new Descriptor("lha", "LHA Archive"));
121 	return descriptors;
122 }
123 
decompress(const PPSystemString & outFilename,Hints hint)124 bool DecompressorLHA::decompress(const PPSystemString& outFilename, Hints hint)
125 {
126 	XMFile f(fileName);
127 
128 	if (!f.isOpen())
129 		return false;
130 
131 	// Create reader object
132 	LHAReaderWrapper reader(f);
133 	if (!reader.isOpen())
134 		return false;
135 
136 	// Loop through each file until we find a sutible module
137 	while (1)
138 	{
139 		LHAFileHeader* header = reader.nextFile();
140 		if (header == NULL)
141 			break;
142 
143 		// Skip directories and symlinks
144 		if (strcmp(header->compress_method, LHA_COMPRESS_TYPE_DIR) == 0)
145 			continue;
146 
147 		// Identify the current file
148 		mp_ubyte buf[LHA_BUFFER_SIZE];
149 		memset(buf, 0, sizeof(buf));
150 		size_t bytes_read = reader.read(buf, sizeof(buf));
151 
152 		if (bytes_read > 0 && XModule::identifyModule(buf) != NULL)
153 		{
154 			// Write to output file
155 			XMFile outFile(outFilename, true);
156 			if (!outFile.isOpenForWriting())
157 				return false;
158 
159 			// Decompress into outFile
160 			do
161 			{
162 				outFile.write(buf, 1, bytes_read);
163 			}
164 			while ((bytes_read = reader.read(buf, sizeof(buf))) > 0);
165 
166 			return (bytes_read == 0);
167 		}
168 	}
169 
170 	// No sutible modules found
171 	return false;
172 }
173 
clone()174 DecompressorBase* DecompressorLHA::clone()
175 {
176 	return new DecompressorLHA(fileName);
177 }
178 
179 static Decompressor::RegisterDecompressor<DecompressorLHA> registerDecompressor;
180