OpenCPN Partial API docs
chartdata_input_stream.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: Support XZ compressed charts
5  * Author: Sean D'Epagnier
6  *
7  ***************************************************************************
8  * Copyright (C) 2016 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  ***************************************************************************
25  *
26  */
27 
28 // For compilers that support precompilation, includes "wx.h".
29 #include <wx/wxprec.h>
30 
31 #ifndef WX_PRECOMP
32 #include <wx/wx.h>
33 #endif // precompiled headers
34 
35 #include <wx/filename.h>
36 #include <wx/log.h>
37 #include <wx/wfstream.h>
38 
39 #include "config.h"
40 #include "model/chartdata_input_stream.h"
41 
42 #ifdef OCPN_USE_LZMA
43 
44 wxCompressedFFileInputStream::wxCompressedFFileInputStream(
45  const wxString &fileName) {
46  init_lzma();
47  m_file = new wxFFile(fileName, "rb");
48 }
49 
50 wxCompressedFFileInputStream::~wxCompressedFFileInputStream() {
51  delete m_file;
52  lzma_end(&strm);
53 }
54 
55 size_t wxCompressedFFileInputStream::OnSysRead(void *buffer, size_t size) {
56  lzma_action action = LZMA_RUN;
57 
58  strm.next_out = (uint8_t *)buffer;
59  strm.avail_out = size;
60 
61  for (;;) {
62  if (strm.avail_in == 0) {
63  if (!m_file->Eof()) {
64  strm.next_in = inbuf;
65  strm.avail_in = m_file->Read(inbuf, sizeof inbuf);
66 
67  if (m_file->Error()) return 0;
68 
69  } else
70  action = LZMA_FINISH;
71  }
72 
73  lzma_ret ret = lzma_code(&strm, action);
74 
75  if (strm.avail_out == 0 || ret == LZMA_STREAM_END)
76  return size - strm.avail_out;
77 
78  if (ret != LZMA_OK) {
79  m_lasterror = wxSTREAM_READ_ERROR;
80  return 0;
81  }
82  }
83  return 0;
84 }
85 
86 wxFileOffset wxCompressedFFileInputStream::OnSysSeek(wxFileOffset pos,
87  wxSeekMode mode) {
88  // rewind to start is possible
89  if (pos == 0 && mode == wxFromStart) {
90  lzma_end(&strm);
91  init_lzma();
92  return m_file->Seek(pos, mode);
93  }
94 
95  return wxInvalidOffset;
96 }
97 
98 wxFileOffset wxCompressedFFileInputStream::OnSysTell() const {
99  return strm.total_out;
100 }
101 
102 void wxCompressedFFileInputStream::init_lzma() {
103  lzma_stream s = LZMA_STREAM_INIT;
104  memcpy(&strm, &s, sizeof s);
105  lzma_ret ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
106 
107  if (ret != LZMA_OK) m_lasterror = wxSTREAM_READ_ERROR;
108 }
109 
110 ChartDataNonSeekableInputStream::ChartDataNonSeekableInputStream(
111  const wxString &fileName) {
112  if (fileName.Upper().EndsWith("XZ"))
113  m_stream = new wxCompressedFFileInputStream(fileName);
114  else
115  m_stream = new wxFFileInputStream(fileName);
116 }
117 
118 ChartDataNonSeekableInputStream::~ChartDataNonSeekableInputStream() {
119  delete m_stream;
120 }
121 
122 size_t ChartDataNonSeekableInputStream::OnSysRead(void *buffer, size_t size) {
123  m_stream->Read(buffer, size);
124  return m_stream->LastRead();
125 }
126 
127 wxFileOffset ChartDataNonSeekableInputStream::OnSysSeek(wxFileOffset pos,
128  wxSeekMode mode) {
129  return m_stream->SeekI(pos, mode);
130 }
131 
132 wxFileOffset ChartDataNonSeekableInputStream::OnSysTell() const {
133  return m_stream->TellI();
134 }
135 
136 ChartDataInputStream::ChartDataInputStream(const wxString &fileName) {
137  if (fileName.Upper().EndsWith("XZ")) {
138  // decompress to temp file to allow seeking
139  m_tempfilename =
140  wxFileName::CreateTempFileName(wxFileName(fileName).GetFullName());
141  wxCompressedFFileInputStream stream(fileName);
142  wxFFileOutputStream tmp(m_tempfilename);
143 
144  char buffer[8192];
145  int len;
146  do {
147  stream.Read(buffer, sizeof buffer);
148  len = stream.LastRead();
149  tmp.Write(buffer, len);
150  } while (len == sizeof buffer);
151 
152  // do some error checking here?
153 
154  tmp.Close();
155  m_stream = new wxFFileInputStream(m_tempfilename);
156  } else
157  m_stream = new wxFFileInputStream(fileName);
158 }
159 
160 ChartDataInputStream::~ChartDataInputStream() {
161  // close it
162  delete m_stream;
163  // delete the temp file, how do we remove temp files if the program crashed?
164  if (!m_tempfilename.empty()) wxRemoveFile(m_tempfilename);
165 }
166 
167 size_t ChartDataInputStream::OnSysRead(void *buffer, size_t size) {
168  m_stream->Read(buffer, size);
169  return m_stream->LastRead();
170 }
171 
172 wxFileOffset ChartDataInputStream::OnSysSeek(wxFileOffset pos,
173  wxSeekMode mode) {
174  return m_stream->SeekI(pos, mode);
175 }
176 
177 wxFileOffset ChartDataInputStream::OnSysTell() const {
178  return m_stream->TellI();
179 }
180 
181 bool DecompressXZFile(const wxString &input_path, const wxString &output_path) {
182  if (!wxFileExists(input_path)) {
183  return false;
184  }
185  wxCompressedFFileInputStream in(input_path);
186  wxFFileOutputStream out(output_path);
187 
188  char buffer[8192];
189  int len;
190  do {
191  in.Read(buffer, sizeof buffer);
192  len = in.LastRead();
193  out.Write(buffer, len);
194  } while (len == sizeof buffer);
195 
196  return in.GetLastError() != wxSTREAM_READ_ERROR;
197 }
198 
199 #else // OCPN_USE_LZMA
200 
201 bool DecompressXZFile(const wxString &input_path, const wxString &output_path) {
202  wxLogMessage(_T("Failed to decompress: ") + input_path);
203  wxLogMessage(_T("OpenCPN compiled without liblzma support"));
204 
205  return false;
206 }
207 
208 #endif // OCPN_USE_LZMA