The Battle for Wesnoth  1.15.1+dev
filesystem_sdl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017-2018 by the Battle for Wesnoth Project https://www.wesnoth.org/
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY.
10 
11  See the COPYING file for more details.
12 */
13 
14 #include <SDL2/SDL.h>
15 #include <SDL2/SDL_rwops.h>
16 
17 #include "filesystem.hpp"
18 #include "log.hpp"
19 
20 #include <algorithm>
21 
22 static lg::log_domain log_filesystem("filesystem");
23 #define ERR_FS LOG_STREAM(err, log_filesystem)
24 
25 namespace filesystem {
26 
27 // Arbitrary numbers larger than 5
28 static const uint32_t read_type = 7;
29 static const uint32_t write_type = 8;
30 
31 static int64_t ifs_size (struct SDL_RWops * context);
32 static int64_t ofs_size (struct SDL_RWops * context);
33 static int64_t SDLCALL ifs_seek(struct SDL_RWops *context, int64_t offset, int whence);
34 static int64_t SDLCALL ofs_seek(struct SDL_RWops *context, int64_t offset, int whence);
35 static std::size_t SDLCALL ifs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum);
36 static std::size_t SDLCALL ofs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum);
37 static std::size_t SDLCALL ifs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num);
38 static std::size_t SDLCALL ofs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num);
39 static int SDLCALL ifs_close(struct SDL_RWops *context);
40 static int SDLCALL ofs_close(struct SDL_RWops *context);
41 
42 rwops_ptr make_read_RWops(const std::string &path) {
43  rwops_ptr rw(SDL_AllocRW(), &SDL_FreeRW);
44 
45  rw->size = &ifs_size;
46  rw->seek = &ifs_seek;
47  rw->read = &ifs_read;
48  rw->write = &ifs_write;
49  rw->close = &ifs_close;
50 
51  rw->type = read_type;
52 
53  scoped_istream ifs = istream_file(path);
54  if(!ifs) {
55  ERR_FS << "make_read_RWops: istream_file returned NULL on " << path << '\n';
56  rw.reset();
57  return rw;
58  }
59 
60  rw->hidden.unknown.data1 = ifs.release();
61 
62  return rw;
63 }
64 
65 rwops_ptr make_write_RWops(const std::string &path) {
66  rwops_ptr rw(SDL_AllocRW(), &SDL_FreeRW);
67 
68  rw->size = &ofs_size;
69  rw->seek = &ofs_seek;
70  rw->read = &ofs_read;
71  rw->write = &ofs_write;
72  rw->close = &ofs_close;
73 
74  rw->type = write_type;
75 
76  scoped_ostream ofs = ostream_file(path);
77  if(!ofs) {
78  ERR_FS << "make_write_RWops: ostream_file returned NULL on " << path << '\n';
79  rw.reset();
80  return rw;
81  }
82 
83  rw->hidden.unknown.data1 = ofs.release();
84 
85  return rw;
86 }
87 
88 static int64_t ifs_size (struct SDL_RWops * context) {
89  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
90  std::streampos orig = ifs->tellg();
91 
92  ifs->seekg(0, std::ios::end);
93 
94  std::streampos len = ifs->tellg();
95 
96  ifs->seekg(orig);
97 
98  return len;
99 }
100 static int64_t ofs_size (struct SDL_RWops * context) {
101  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
102  std::streampos orig = ofs->tellp();
103 
104  ofs->seekp(0, std::ios::end);
105 
106  std::streampos len = ofs->tellp();
107 
108  ofs->seekp(orig);
109 
110  return len;
111 }
112 
113 typedef std::pair<int64_t, std::ios_base::seekdir> offset_dir;
114 
115 static offset_dir translate_seekdir(int64_t offset, int whence) {
116  switch(whence){
117  case RW_SEEK_SET:
118  return std::make_pair(std::max<int64_t>(0, offset), std::ios_base::beg);
119  case RW_SEEK_CUR:
120  return std::make_pair(offset, std::ios_base::cur);
121  case RW_SEEK_END:
122  return std::make_pair(std::min<int64_t>(0, offset), std::ios_base::end);
123  default:
124  assert(false);
125  throw "assertion ignored";
126  }
127 }
128 static int64_t SDLCALL ifs_seek(struct SDL_RWops *context, int64_t offset, int whence) {
129  std::ios_base::seekdir seekdir;
130  std::tie(offset, seekdir) = translate_seekdir(offset, whence);
131 
132  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
133  const std::ios_base::iostate saved_state = ifs->rdstate();
134 
135  ifs->seekg(offset, seekdir);
136 
137  if(saved_state != ifs->rdstate() && offset < 0) {
138  ifs->clear(saved_state);
139  ifs->seekg(0, std::ios_base::beg);
140  }
141 
142  std::streamsize pos = ifs->tellg();
143  return static_cast<int>(pos);
144 }
145 static int64_t SDLCALL ofs_seek(struct SDL_RWops *context, int64_t offset, int whence) {
146  std::ios_base::seekdir seekdir;
147  std::tie(offset, seekdir) = translate_seekdir(offset, whence);
148 
149  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
150  const std::ios_base::iostate saved_state = ofs->rdstate();
151 
152  ofs->seekp(offset, seekdir);
153 
154  if(saved_state != ofs->rdstate() && offset < 0) {
155  ofs->clear(saved_state);
156  ofs->seekp(0, std::ios_base::beg);
157  }
158 
159  std::streamsize pos = ofs->tellp();
160  return static_cast<int>(pos);
161 }
162 
163 static std::size_t SDLCALL ifs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum) {
164  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
165 
166  // This seems overly simplistic, but it's the same as mem_read's implementation
167  ifs->read(static_cast<char*>(ptr), maxnum * size);
168  std::streamsize num = ifs->good() ? maxnum : ifs->gcount() / size;
169 
170  // EOF sticks unless we clear it. Bad is an actual I/O error
171  if(!ifs->bad())
172  ifs->clear();
173 
174  return static_cast<int>(num);
175 }
176 static std::size_t SDLCALL ofs_read(struct SDL_RWops * /*context*/, void * /*ptr*/, std::size_t /*size*/, std::size_t /*maxnum*/) {
177  SDL_SetError("Reading not implemented");
178  return 0;
179 }
180 
181 static std::size_t SDLCALL ifs_write(struct SDL_RWops * /*context*/, const void * /*ptr*/, std::size_t /*size*/, std::size_t /*num*/) {
182  SDL_SetError("Writing not implemented");
183  return 0;
184 }
185 static std::size_t SDLCALL ofs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num) {
186  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
187 
188  const std::streampos before = ofs->tellp();
189  ofs->write(static_cast<const char*>(ptr), num * size);
190  const std::streampos after = ofs->tellp();
191  const std::streamoff bytes_written = after - before;
192  const int num_written = bytes_written / size;
193 
194  return num_written;
195 }
196 
197 static int SDLCALL ifs_close(struct SDL_RWops *context) {
198  if (context) {
199  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
200  delete ifs;
201  SDL_FreeRW(context);
202  }
203  return 0;
204 }
205 static int SDLCALL ofs_close(struct SDL_RWops *context) {
206  if (context) {
207  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
208  delete ofs;
209  SDL_FreeRW(context);
210  }
211  return 0;
212 }
213 
214 }
static int SDLCALL ofs_close(struct SDL_RWops *context)
static offset_dir translate_seekdir(int64_t offset, int whence)
static int SDLCALL ifs_close(struct SDL_RWops *context)
std::pair< int64_t, std::ios_base::seekdir > offset_dir
static std::size_t SDLCALL ofs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num)
rwops_ptr make_read_RWops(const std::string &path)
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
Definition: filesystem.cpp:925
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
rwops_ptr make_write_RWops(const std::string &path)
static std::size_t SDLCALL ifs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
std::string path
Definition: game_config.cpp:39
static std::size_t SDLCALL ifs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num)
static int64_t SDLCALL ofs_seek(struct SDL_RWops *context, int64_t offset, int whence)
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:40
static std::size_t SDLCALL ofs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum)
Declarations for File-IO.
std::unique_ptr< SDL_RWops, void(*)(SDL_RWops *)> rwops_ptr
Definition: filesystem.hpp:42
static int64_t ifs_size(struct SDL_RWops *context)
static const uint32_t write_type
Standard logging facilities (interface).
static const uint32_t read_type
#define ERR_FS
static int64_t SDLCALL ifs_seek(struct SDL_RWops *context, int64_t offset, int whence)
static lg::log_domain log_filesystem("filesystem")
static int64_t ofs_size(struct SDL_RWops *context)
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory)
Definition: filesystem.cpp:963