Merge commit 'master@6ed15c5a41130b55cb57a43a8a9470a91d38c3d5' into blender2.8
[blender.git] / source / blender / alembic / intern / abc_archive.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software  Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2016 K√©vin Dietrich.
19  * All rights reserved.
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  *
23  */
24
25 #include "abc_archive.h"
26
27 #ifdef WIN32
28 #  include "utfconv.h"
29 #endif
30
31 #include <fstream>
32
33 using Alembic::Abc::Exception;
34 using Alembic::Abc::ErrorHandler;
35 using Alembic::Abc::IArchive;
36 using Alembic::Abc::kWrapExisting;
37 using Alembic::Abc::OArchive;
38
39 static IArchive open_archive(const std::string &filename,
40                              const std::vector<std::istream *> &input_streams,
41                              bool &is_hdf5)
42 {
43         is_hdf5 = false;
44
45         try {
46                 Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams);
47
48                 return IArchive(archive_reader(filename),
49                                 kWrapExisting,
50                                 ErrorHandler::kThrowPolicy);
51         }
52         catch (const Exception &e) {
53                 std::cerr << e.what() << '\n';
54
55 #ifdef WITH_ALEMBIC_HDF5
56                 try {
57                         is_hdf5 = true;
58                         Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
59
60                         return IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
61                                         filename.c_str(), ErrorHandler::kThrowPolicy,
62                                         cache_ptr);
63                 }
64                 catch (const Exception &) {
65                         std::cerr << e.what() << '\n';
66                         return IArchive();
67                 }
68 #else
69                 /* Inspect the file to see whether it's really a HDF5 file. */
70                 char header[4];  /* char(0x89) + "HDF" */
71                 std::ifstream the_file(filename.c_str(), std::ios::in | std::ios::binary);
72                 if (!the_file) {
73                         std::cerr << "Unable to open " << filename << std::endl;
74                 }
75                 else if (!the_file.read(header, sizeof(header))) {
76                         std::cerr << "Unable to read from " << filename << std::endl;
77                 }
78                 else if (strncmp(header + 1, "HDF", 3)) {
79                         std::cerr << filename << " has an unknown file format, unable to read." << std::endl;
80                 }
81                 else {
82                         is_hdf5 = true;
83                         std::cerr << filename << " is in the obsolete HDF5 format, unable to read." << std::endl;
84                 }
85
86                 if (the_file.is_open()) {
87                         the_file.close();
88                 }
89
90                 return IArchive();
91 #endif
92         }
93
94         return IArchive();
95 }
96
97 ArchiveReader::ArchiveReader(const char *filename)
98 {
99 #ifdef WIN32
100         UTF16_ENCODE(filename);
101         std::wstring wstr(filename_16);
102         m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary);
103         UTF16_UN_ENCODE(filename);
104 #else
105         m_infile.open(filename, std::ios::in | std::ios::binary);
106 #endif
107
108         m_streams.push_back(&m_infile);
109
110         m_archive = open_archive(filename, m_streams, m_is_hdf5);
111
112         /* We can't open an HDF5 file from a stream, so close it. */
113         if (m_is_hdf5) {
114                 m_infile.close();
115                 m_streams.clear();
116         }
117 }
118
119 bool ArchiveReader::is_hdf5() const
120 {
121         return m_is_hdf5;
122 }
123
124 bool ArchiveReader::valid() const
125 {
126         return m_archive.valid();
127 }
128
129 Alembic::Abc::IObject ArchiveReader::getTop()
130 {
131         return m_archive.getTop();
132 }
133
134 /* ************************************************************************** */
135
136 /* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
137  * have a version supporting streams. */
138 static OArchive create_archive(std::ostream *ostream,
139                                const std::string &filename,
140                                const std::string &scene_name,
141                                Alembic::Abc::MetaData &md,
142                                bool ogawa)
143 {
144         md.set(Alembic::Abc::kApplicationNameKey, "Blender");
145         md.set(Alembic::Abc::kUserDescriptionKey, scene_name);
146
147         time_t raw_time;
148         time(&raw_time);
149         char buffer[128];
150
151 #if defined _WIN32 || defined _WIN64
152         ctime_s(buffer, 128, &raw_time);
153 #else
154         ctime_r(&raw_time, buffer);
155 #endif
156
157         const std::size_t buffer_len = strlen(buffer);
158         if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
159                 buffer[buffer_len - 1] = '\0';
160         }
161
162         md.set(Alembic::Abc::kDateWrittenKey, buffer);
163
164         ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
165
166 #ifdef WITH_ALEMBIC_HDF5
167         if (!ogawa) {
168                 return OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, md, policy);
169         }
170 #else
171         static_cast<void>(filename);
172         static_cast<void>(ogawa);
173 #endif
174
175         Alembic::AbcCoreOgawa::WriteArchive archive_writer;
176         return OArchive(archive_writer(ostream, md), kWrapExisting, policy);
177 }
178
179 ArchiveWriter::ArchiveWriter(const char *filename, const char *scene, bool do_ogawa, Alembic::Abc::MetaData &md)
180 {
181         /* Use stream to support unicode character paths on Windows. */
182         if (do_ogawa) {
183 #ifdef WIN32
184                 UTF16_ENCODE(filename);
185                 std::wstring wstr(filename_16);
186                 m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary);
187                 UTF16_UN_ENCODE(filename);
188 #else
189                 m_outfile.open(filename, std::ios::out | std::ios::binary);
190 #endif
191         }
192
193         m_archive = create_archive(&m_outfile,
194                                    filename,
195                                    scene,
196                                    md,
197                                    do_ogawa);
198 }
199
200 OArchive &ArchiveWriter::archive()
201 {
202         return m_archive;
203 }