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