The Battle for Wesnoth  1.17.23+dev
filesystem_sdl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2023
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 void sdl_rwops_deleter::operator()(SDL_RWops* p) const noexcept
45 {
46  SDL_FreeRW(p);
47 }
48 
49 rwops_ptr make_read_RWops(const std::string &path) {
50  rwops_ptr rw(SDL_AllocRW());
51 
52  rw->size = &ifs_size;
53  rw->seek = &ifs_seek;
54  rw->read = &ifs_read;
55  rw->write = &ifs_write;
56  rw->close = &ifs_close;
57 
58  rw->type = read_type;
59 
61  if(!ifs) {
62  ERR_FS << "make_read_RWops: istream_file returned NULL on " << path;
63  rw.reset();
64  return rw;
65  }
66 
67  rw->hidden.unknown.data1 = ifs.release();
68 
69  return rw;
70 }
71 
72 rwops_ptr make_write_RWops(const std::string &path) {
73  rwops_ptr rw(SDL_AllocRW());
74 
75  rw->size = &ofs_size;
76  rw->seek = &ofs_seek;
77  rw->read = &ofs_read;
78  rw->write = &ofs_write;
79  rw->close = &ofs_close;
80 
81  rw->type = write_type;
82 
84  if(!ofs) {
85  ERR_FS << "make_write_RWops: ostream_file returned NULL on " << path;
86  rw.reset();
87  return rw;
88  }
89 
90  rw->hidden.unknown.data1 = ofs.release();
91 
92  return rw;
93 }
94 
95 static int64_t ifs_size (struct SDL_RWops * context) {
96  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
97  std::streampos orig = ifs->tellg();
98 
99  ifs->seekg(0, std::ios::end);
100 
101  std::streampos len = ifs->tellg();
102 
103  ifs->seekg(orig);
104 
105  return len;
106 }
107 static int64_t ofs_size (struct SDL_RWops * context) {
108  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
109  std::streampos orig = ofs->tellp();
110 
111  ofs->seekp(0, std::ios::end);
112 
113  std::streampos len = ofs->tellp();
114 
115  ofs->seekp(orig);
116 
117  return len;
118 }
119 
120 typedef std::pair<int64_t, std::ios_base::seekdir> offset_dir;
121 
122 static offset_dir translate_seekdir(int64_t offset, int whence) {
123  switch(whence){
124  case RW_SEEK_SET:
125  return std::pair(std::max<int64_t>(0, offset), std::ios_base::beg);
126  case RW_SEEK_CUR:
127  return std::pair(offset, std::ios_base::cur);
128  case RW_SEEK_END:
129  return std::pair(std::min<int64_t>(0, offset), std::ios_base::end);
130  default:
131  assert(false);
132  throw "assertion ignored";
133  }
134 }
135 static int64_t SDLCALL ifs_seek(struct SDL_RWops *context, int64_t offset, int whence) {
136  std::ios_base::seekdir seekdir;
137  std::tie(offset, seekdir) = translate_seekdir(offset, whence);
138 
139  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
140  const std::ios_base::iostate saved_state = ifs->rdstate();
141 
142  ifs->seekg(offset, seekdir);
143 
144  if(saved_state != ifs->rdstate() && offset < 0) {
145  ifs->clear(saved_state);
146  ifs->seekg(0, std::ios_base::beg);
147  }
148 
149  std::streamsize pos = ifs->tellg();
150  return static_cast<int>(pos);
151 }
152 static int64_t SDLCALL ofs_seek(struct SDL_RWops *context, int64_t offset, int whence) {
153  std::ios_base::seekdir seekdir;
154  std::tie(offset, seekdir) = translate_seekdir(offset, whence);
155 
156  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
157  const std::ios_base::iostate saved_state = ofs->rdstate();
158 
159  ofs->seekp(offset, seekdir);
160 
161  if(saved_state != ofs->rdstate() && offset < 0) {
162  ofs->clear(saved_state);
163  ofs->seekp(0, std::ios_base::beg);
164  }
165 
166  std::streamsize pos = ofs->tellp();
167  return static_cast<int>(pos);
168 }
169 
170 static std::size_t SDLCALL ifs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum) {
171  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
172 
173  // This seems overly simplistic, but it's the same as mem_read's implementation
174  ifs->read(static_cast<char*>(ptr), maxnum * size);
175  std::streamsize num = ifs->good() ? maxnum : ifs->gcount() / size;
176 
177  // EOF sticks unless we clear it. Bad is an actual I/O error
178  if(!ifs->bad())
179  ifs->clear();
180 
181  return static_cast<int>(num);
182 }
183 static std::size_t SDLCALL ofs_read(struct SDL_RWops * /*context*/, void * /*ptr*/, std::size_t /*size*/, std::size_t /*maxnum*/) {
184  SDL_SetError("Reading not implemented");
185  return 0;
186 }
187 
188 static std::size_t SDLCALL ifs_write(struct SDL_RWops * /*context*/, const void * /*ptr*/, std::size_t /*size*/, std::size_t /*num*/) {
189  SDL_SetError("Writing not implemented");
190  return 0;
191 }
192 static std::size_t SDLCALL ofs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num) {
193  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
194 
195  const std::streampos before = ofs->tellp();
196  ofs->write(static_cast<const char*>(ptr), num * size);
197  const std::streampos after = ofs->tellp();
198  const std::streamoff bytes_written = after - before;
199  const int num_written = bytes_written / size;
200 
201  return num_written;
202 }
203 
204 static int SDLCALL ifs_close(struct SDL_RWops *context) {
205  if (context) {
206  std::istream *ifs = static_cast<std::istream*>(context->hidden.unknown.data1);
207  delete ifs;
208  SDL_FreeRW(context);
209  }
210  return 0;
211 }
212 static int SDLCALL ofs_close(struct SDL_RWops *context) {
213  if (context) {
214  std::ostream *ofs = static_cast<std::ostream*>(context->hidden.unknown.data1);
215  delete ofs;
216  SDL_FreeRW(context);
217  }
218  return 0;
219 }
220 
221 }
Declarations for File-IO.
static lg::log_domain log_filesystem("filesystem")
#define ERR_FS
Standard logging facilities (interface).
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
static int64_t ifs_size(struct SDL_RWops *context)
std::pair< int64_t, std::ios_base::seekdir > offset_dir
static int64_t SDLCALL ofs_seek(struct SDL_RWops *context, int64_t offset, int whence)
static std::size_t SDLCALL ofs_read(struct SDL_RWops *context, void *ptr, std::size_t size, std::size_t maxnum)
static const uint32_t read_type
std::unique_ptr< SDL_RWops, sdl_rwops_deleter > rwops_ptr
Definition: filesystem.hpp:58
rwops_ptr make_read_RWops(const std::string &path)
static int64_t ofs_size(struct SDL_RWops *context)
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
static int SDLCALL ifs_close(struct SDL_RWops *context)
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:50
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:51
static int SDLCALL ofs_close(struct SDL_RWops *context)
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_write_RWops(const std::string &path)
static const uint32_t write_type
static std::size_t SDLCALL ifs_write(struct SDL_RWops *context, const void *ptr, std::size_t size, std::size_t num)
static offset_dir translate_seekdir(int64_t offset, int whence)
static int64_t SDLCALL ifs_seek(struct SDL_RWops *context, int64_t offset, int whence)
std::string path
Definition: filesystem.cpp:86
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
void operator()(SDL_RWops *) const noexcept
mock_party p