Merge branch 'master' 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 extern "C"
27 {
28         #include "BKE_blender_version.h"
29 }
30
31 #ifdef WIN32
32 #  include "utfconv.h"
33 #endif
34
35 #include <fstream>
36
37 using Alembic::Abc::Exception;
38 using Alembic::Abc::ErrorHandler;
39 using Alembic::Abc::IArchive;
40 using Alembic::Abc::kWrapExisting;
41 using Alembic::Abc::OArchive;
42
43 static IArchive open_archive(const std::string &filename,
44                              const std::vector<std::istream *> &input_streams,
45                              bool &is_hdf5)
46 {
47         is_hdf5 = false;
48
49         try {
50                 Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams);
51
52                 return IArchive(archive_reader(filename),
53                                 kWrapExisting,
54                                 ErrorHandler::kThrowPolicy);
55         }
56         catch (const Exception &e) {
57                 std::cerr << e.what() << '\n';
58
59 #ifdef WITH_ALEMBIC_HDF5
60                 try {
61                         is_hdf5 = true;
62                         Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
63
64                         return IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
65                                         filename.c_str(), ErrorHandler::kThrowPolicy,
66                                         cache_ptr);
67                 }
68                 catch (const Exception &) {
69                         std::cerr << e.what() << '\n';
70                         return IArchive();
71                 }
72 #else
73                 /* Inspect the file to see whether it's really a HDF5 file. */
74                 char header[4];  /* char(0x89) + "HDF" */
75                 std::ifstream the_file(filename.c_str(), std::ios::in | std::ios::binary);
76                 if (!the_file) {
77                         std::cerr << "Unable to open " << filename << std::endl;
78                 }
79                 else if (!the_file.read(header, sizeof(header))) {
80                         std::cerr << "Unable to read from " << filename << std::endl;
81                 }
82                 else if (strncmp(header + 1, "HDF", 3)) {
83                         std::cerr << filename << " has an unknown file format, unable to read." << std::endl;
84                 }
85                 else {
86                         is_hdf5 = true;
87                         std::cerr << filename << " is in the obsolete HDF5 format, unable to read." << std::endl;
88                 }
89
90                 if (the_file.is_open()) {
91                         the_file.close();
92                 }
93
94                 return IArchive();
95 #endif
96         }
97
98         return IArchive();
99 }
100
101 ArchiveReader::ArchiveReader(const char *filename)
102 {
103 #ifdef WIN32
104         UTF16_ENCODE(filename);
105         std::wstring wstr(filename_16);
106         m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary);
107         UTF16_UN_ENCODE(filename);
108 #else
109         m_infile.open(filename, std::ios::in | std::ios::binary);
110 #endif
111
112         m_streams.push_back(&m_infile);
113
114         m_archive = open_archive(filename, m_streams, m_is_hdf5);
115
116         /* We can't open an HDF5 file from a stream, so close it. */
117         if (m_is_hdf5) {
118                 m_infile.close();
119                 m_streams.clear();
120         }
121 }
122
123 bool ArchiveReader::is_hdf5() const
124 {
125         return m_is_hdf5;
126 }
127
128 bool ArchiveReader::valid() const
129 {
130         return m_archive.valid();
131 }
132
133 Alembic::Abc::IObject ArchiveReader::getTop()
134 {
135         return m_archive.getTop();
136 }
137
138 /* ************************************************************************** */
139
140 /* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
141  * have a version supporting streams. */
142 static OArchive create_archive(std::ostream *ostream,
143                                const std::string &filename,
144                                const std::string &scene_name,
145                                Alembic::Abc::MetaData &md,
146                                bool ogawa)
147 {
148         md.set(Alembic::Abc::kApplicationNameKey, "Blender");
149         md.set(Alembic::Abc::kUserDescriptionKey, scene_name);
150         md.set("blender_version", versionstr);
151
152         time_t raw_time;
153         time(&raw_time);
154         char buffer[128];
155
156 #if defined _WIN32 || defined _WIN64
157         ctime_s(buffer, 128, &raw_time);
158 #else
159         ctime_r(&raw_time, buffer);
160 #endif
161
162         const std::size_t buffer_len = strlen(buffer);
163         if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
164                 buffer[buffer_len - 1] = '\0';
165         }
166
167         md.set(Alembic::Abc::kDateWrittenKey, buffer);
168
169         ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
170
171 #ifdef WITH_ALEMBIC_HDF5
172         if (!ogawa) {
173                 return OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, md, policy);
174         }
175 #else
176         static_cast<void>(filename);
177         static_cast<void>(ogawa);
178 #endif
179
180         Alembic::AbcCoreOgawa::WriteArchive archive_writer;
181         return OArchive(archive_writer(ostream, md), kWrapExisting, policy);
182 }
183
184 ArchiveWriter::ArchiveWriter(const char *filename, const char *scene, bool do_ogawa, Alembic::Abc::MetaData &md)
185 {
186         /* Use stream to support unicode character paths on Windows. */
187         if (do_ogawa) {
188 #ifdef WIN32
189                 UTF16_ENCODE(filename);
190                 std::wstring wstr(filename_16);
191                 m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary);
192                 UTF16_UN_ENCODE(filename);
193 #else
194                 m_outfile.open(filename, std::ios::out | std::ios::binary);
195 #endif
196         }
197
198         m_archive = create_archive(&m_outfile,
199                                    filename,
200                                    scene,
201                                    md,
202                                    do_ogawa);
203 }
204
205 OArchive &ArchiveWriter::archive()
206 {
207         return m_archive;
208 }