aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.cpp
blob: e243d1d177eb13f304aec392b23e55b072b67236 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
 * unlha.cpp
 * ---------
 * Purpose: Implementation file for extracting modules from .lha archives, making use of lhasa
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */

#include "stdafx.h"
#include "unlha.h"

#ifdef MPT_WITH_LHASA
#include "lhasa.h"
#endif // MPT_WITH_LHASA


OPENMPT_NAMESPACE_BEGIN


#ifdef MPT_WITH_LHASA


static int LHAreadFileReader(void *handle, void *buf, size_t buf_len)
{
	FileReader *f = reinterpret_cast<FileReader*>(handle);
	int read_len = mpt::saturate_cast<int>(buf_len);
	int result = mpt::saturate_cast<int>(f->ReadRaw(mpt::span(mpt::void_cast<std::byte*>(buf), read_len)).size());
	if(result == 0)
	{
		return -1;
	}
	return result;
}

static int LHAskipFileReader(void *handle, size_t bytes)
{
	FileReader *f = reinterpret_cast<FileReader*>(handle);
	if(f->CanRead(bytes))
	{
		f->Skip(bytes);
		return 1;
	}
	return 0;
}

static void LHAcloseFileReader(void * /*handle*/)
{
	return;
}

static LHAInputStreamType vtable =
{
	LHAreadFileReader,
	LHAskipFileReader,
	LHAcloseFileReader
};


CLhaArchive::CLhaArchive(FileReader &file) : ArchiveBase(file), inputstream(nullptr), reader(nullptr), firstfile(nullptr)
{
	OpenArchive();
	for(LHAFileHeader *fileheader = firstfile; fileheader; fileheader = lha_reader_next_file(reader))
	{
		ArchiveFileInfo info;
		info.name = mpt::PathString::FromUnicode(mpt::ToUnicode(mpt::Charset::Amiga_no_C1, fileheader->filename));
		info.size = fileheader->length;
		info.type = ArchiveFileType::Normal;
		contents.push_back(info);
	}
	CloseArchive();
}


CLhaArchive::~CLhaArchive()
{
	return;
}


void CLhaArchive::OpenArchive()
{
	inFile.Rewind();
	inputstream = lha_input_stream_new(&vtable, &inFile);
	if(inputstream)
	{
		reader = lha_reader_new(inputstream);
	}
	if(reader)
	{
		lha_reader_set_dir_policy(reader, LHA_READER_DIR_END_OF_DIR);
		firstfile = lha_reader_next_file(reader);
	}
}


void CLhaArchive::CloseArchive()
{
	if(reader)
	{
		lha_reader_free(reader);
		reader = nullptr;
	}
	if(inputstream)
	{
		lha_input_stream_free(inputstream);
		inputstream = nullptr;
	}
}


bool CLhaArchive::ExtractFile(std::size_t index)
{
	if(index >= contents.size())
	{
		return false;
	}
	data.clear();
	OpenArchive();
	const std::size_t bufSize = 4096;
	std::size_t i = 0;
	for(LHAFileHeader *fileheader = firstfile; fileheader; fileheader = lha_reader_next_file(reader))
	{
		if(index == i)
		{
			data.clear();
			std::size_t countRead = 0;
			do
			{
				try
				{
					data.resize(data.size() + bufSize);
				} catch(...)
				{
					CloseArchive();
					return false;
				}
				countRead = lha_reader_read(reader, &data[data.size() - bufSize], bufSize);
				if(countRead < bufSize)
				{
					try
					{
						data.resize(data.size() - (bufSize - countRead));
					} catch(...)
					{
						CloseArchive();
						return false;
					}
				}
			} while(countRead > 0);
		}
		++i;
	}
	CloseArchive();
	return data.size() > 0;
}


#endif // MPT_WITH_LHASA


OPENMPT_NAMESPACE_END