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