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