summaryrefslogtreecommitdiff
path: root/src/random_access_file.cpp
blob: 5abfe7d9b4ae8d31b322d5d00118418097ca6a33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * This file is part of OpenTTD.
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 */

 /** @file random_access_file.cpp Actual implementation of the RandomAccessFile class. */

#include "stdafx.h"
#include "random_access_file_type.h"

#include "debug.h"
#include "fileio_func.h"
#include "string_func.h"

#include "safeguards.h"

/**
 * Create the RandomAccesFile.
 * @param filename Name of the file at the disk.
 * @param subdir   The sub directory to search this file in.
 */
RandomAccessFile::RandomAccessFile(const std::string &filename, Subdirectory subdir) : filename(filename)
{
	this->file_handle = FioFOpenFile(filename, "rb", subdir);
	if (this->file_handle == nullptr) usererror("Cannot open file '%s'", filename.c_str());

	/* When files are in a tar-file, the begin of the file might not be at 0. */
	long pos = ftell(this->file_handle);
	if (pos < 0) usererror("Cannot read file '%s'", filename.c_str());

	/* Store the filename without path and extension */
	auto t = filename.rfind(PATHSEPCHAR);
	std::string name_without_path = filename.substr(t != std::string::npos ? t + 1 : 0);
	this->simplified_filename = name_without_path.substr(0, name_without_path.rfind('.'));
	strtolower(this->simplified_filename);

	this->SeekTo((size_t)pos, SEEK_SET);
}

/**
 * Close the file's file handle.
 */
RandomAccessFile::~RandomAccessFile()
{
	fclose(this->file_handle);
}

/**
 * Get the filename of the opened file with the path from the SubDirectory and the extension.
 * @return Name of the file.
 */
const std::string &RandomAccessFile::GetFilename() const
{
	return this->filename;
}

/**
 * Get the simplified filename of the opened file. The simplified filename is the name of the
 * file without the SubDirectory or extension in lower case.
 * @return Name of the file.
 */
const std::string &RandomAccessFile::GetSimplifiedFilename() const
{
	return this->simplified_filename;
}

/**
 * Get position in the file.
 * @return Position in the file.
 */
size_t RandomAccessFile::GetPos() const
{
	return this->pos + (this->buffer - this->buffer_end);
}

/**
 * Seek in the current file.
 * @param pos New position.
 * @param mode Type of seek (\c SEEK_CUR means \a pos is relative to current position, \c SEEK_SET means \a pos is absolute).
 */
void RandomAccessFile::SeekTo(size_t pos, int mode)
{
	if (mode == SEEK_CUR) pos += this->GetPos();

	this->pos = pos;
	if (fseek(this->file_handle, this->pos, SEEK_SET) < 0) {
		Debug(misc, 0, "Seeking in {} failed", this->filename);
	}

	/* Reset the buffer, so the next ReadByte will read bytes from the file. */
	this->buffer = this->buffer_end = this->buffer_start;
}

/**
 * Read a byte from the file.
 * @return Read byte.
 */
byte RandomAccessFile::ReadByte()
{
	if (this->buffer == this->buffer_end) {
		this->buffer = this->buffer_start;
		size_t size = fread(this->buffer, 1, RandomAccessFile::BUFFER_SIZE, this->file_handle);
		this->pos += size;
		this->buffer_end = this->buffer_start + size;

		if (size == 0) return 0;
	}
	return *this->buffer++;
}

/**
 * Read a word (16 bits) from the file (in low endian format).
 * @return Read word.
 */
uint16 RandomAccessFile::ReadWord()
{
	byte b = this->ReadByte();
	return (this->ReadByte() << 8) | b;
}

/**
 * Read a double word (32 bits) from the file (in low endian format).
 * @return Read word.
 */
uint32 RandomAccessFile::ReadDword()
{
	uint b = this->ReadWord();
	return (this->ReadWord() << 16) | b;
}

/**
 * Read a block.
 * @param ptr  Destination buffer.
 * @param size Number of bytes to read.
 */
void RandomAccessFile::ReadBlock(void *ptr, size_t size)
{
	this->SeekTo(this->GetPos(), SEEK_SET);
	this->pos += fread(ptr, 1, size, this->file_handle);
}

/**
 * Skip \a n bytes ahead in the file.
 * @param n Number of bytes to skip reading.
 */
void RandomAccessFile::SkipBytes(int n)
{
	int remaining = this->buffer_end - this->buffer;
	if (n <= remaining) {
		this->buffer += n;
	} else {
		this->SeekTo(n, SEEK_CUR);
	}
}