forked from teamnwah/openmw-tes3coop
Started converting terrain code to D.
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@119 ea6a568a-9f4f-0410-981a-c910a81bb256actorid
parent
5c523995bb
commit
61030fc382
@ -0,0 +1,463 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2009 Nicolay Korslund
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (archive.d) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
// This should also be part of the generic cache system.
|
||||
const int CACHE_MAGIC = 0x345AF815;
|
||||
|
||||
import std.mmfile;
|
||||
import std.stream;
|
||||
import std.string;
|
||||
|
||||
version(Windows)
|
||||
static int pageSize = 64*1024;
|
||||
else
|
||||
static int pageSize = 4*1024;
|
||||
|
||||
// Info about the entire quad. TODO: Some of this (such as the texture
|
||||
// scale and probably the width and radius) can be generated at
|
||||
// loadtime and is common for all quads on the same level. We could
|
||||
// just make a QuadLevelInfo struct.
|
||||
struct QuadInfo
|
||||
{
|
||||
// Basic info
|
||||
int cellX, cellY;
|
||||
int level;
|
||||
|
||||
// Bounding box info
|
||||
float minHeight, maxHeight;
|
||||
float worldWidth;
|
||||
float boundingRadius;
|
||||
|
||||
// Texture scale for this quad
|
||||
float texScale;
|
||||
|
||||
// True if we should make the given child
|
||||
bool hasChild[4];
|
||||
|
||||
// Number of mesh segments in this quad
|
||||
int meshNum;
|
||||
|
||||
// Location of this quad in the main archive file. The size includes
|
||||
// everything related to this quad, including mesh data, alpha maps,
|
||||
// etc.
|
||||
size_t offset, size;
|
||||
}
|
||||
|
||||
|
||||
// Info about an alpha map belonging to a mesh
|
||||
struct AlphaInfo
|
||||
{
|
||||
size_t bufSize, bufOffset;
|
||||
|
||||
// The texture name for this layer. The actual string is stored in
|
||||
// the archive's string buffer.
|
||||
int texName;
|
||||
int alphaName;
|
||||
|
||||
// Fill the alpha texture buffer
|
||||
void fillAlphaBuffer(ubyte *abuf)
|
||||
{
|
||||
g_archive.copy(abuf, bufOffset, bufSize);
|
||||
}
|
||||
|
||||
// Get the texture for this alpha layer
|
||||
char[] getTexName()
|
||||
{
|
||||
return g_archive.getString(texName);
|
||||
}
|
||||
|
||||
// Get the material name to give the alpha texture
|
||||
char[] getAlphaName()
|
||||
{
|
||||
return g_archive.getString(alphaName);
|
||||
}
|
||||
}
|
||||
|
||||
// Info about each submesh
|
||||
struct MeshInfo
|
||||
{
|
||||
// Bounding box info
|
||||
float minHeight, maxHeight;
|
||||
float worldWidth;
|
||||
|
||||
// Vertex and index numbers
|
||||
int vertRows, vertCols;
|
||||
int indexCount;
|
||||
|
||||
// Scene node position (relative to the parent node)
|
||||
float x, y;
|
||||
|
||||
// Height offset to apply to all vertices
|
||||
float heightOffset;
|
||||
|
||||
// Size and offset of the vertex buffer
|
||||
size_t vertBufSize, vertBufOffset;
|
||||
|
||||
// Number and offset of AlphaInfo blocks
|
||||
int alphaNum;
|
||||
size_t alphaOffset;
|
||||
|
||||
// Texture name. Index to the string table.
|
||||
int texName;
|
||||
|
||||
// Fill the given vertex buffer
|
||||
void fillVertexBuffer(float *vbuf)
|
||||
{
|
||||
//g_archive.copy(vbuf, vertBufOffset, vertBufSize);
|
||||
|
||||
// The height map and normals from the archive
|
||||
char *hmap = cast(char*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr;
|
||||
|
||||
int level = getLevel();
|
||||
|
||||
// The generic part, containing the x,y coordinates and the uv
|
||||
// maps.
|
||||
float *gmap = g_archive.getVertexBuffer(level).ptr;
|
||||
|
||||
// Calculate the factor to multiply each height value with. The
|
||||
// heights are very limited in range as they are stored in a
|
||||
// single byte. Normal MW data uses a factor of 8, but we have to
|
||||
// double this for each successive level since we're splicing
|
||||
// several vertices together and need to allow larger differences
|
||||
// for each vertex. The formula is 8*2^(level-1).
|
||||
float scale = 4.0 * (1<<level);
|
||||
|
||||
// Merge the two data sets together into the output buffer.
|
||||
float offset = heightOffset;
|
||||
for(int y=0; y<vertRows; y++)
|
||||
{
|
||||
// The offset for the entire row is determined by the first
|
||||
// height value. All the values in a row gives the height
|
||||
// relative to the previous value, and the first value in each
|
||||
// row is relative to the first value in the previous row.
|
||||
offset += *hmap;
|
||||
|
||||
// This is the 'sliding offset' for this row. It's adjusted
|
||||
// for each vertex that's added, but only affects this row.
|
||||
float rowofs = offset;
|
||||
for(int x=0; x<vertCols; x++)
|
||||
{
|
||||
hmap++; // Skip the byte we just read
|
||||
|
||||
// X and Y from the pregenerated buffer
|
||||
*vbuf++ = *gmap++;
|
||||
*vbuf++ = *gmap++;
|
||||
|
||||
// The height is calculated from the current offset
|
||||
*vbuf++ = rowofs * scale;
|
||||
|
||||
// Normal vector.
|
||||
// TODO: Normalize?
|
||||
*vbuf++ = *hmap++;
|
||||
*vbuf++ = *hmap++;
|
||||
*vbuf++ = *hmap++;
|
||||
|
||||
// UV
|
||||
*vbuf++ = *gmap++;
|
||||
*vbuf++ = *gmap++;
|
||||
|
||||
// Adjust the offset for the next vertex. On the last
|
||||
// iteration this will read past the current row, but
|
||||
// that's OK since rowofs is discarded afterwards.
|
||||
rowofs += *hmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the index buffer
|
||||
void fillIndexBuffer(ushort *ibuf)
|
||||
{
|
||||
// The index buffer is pregenerated. It is identical for all
|
||||
// meshes on the same level, so just copy it over.
|
||||
ushort generic[] = g_archive.getIndexBuffer(getLevel());
|
||||
ibuf[0..generic.length] = generic[];
|
||||
}
|
||||
|
||||
int getLevel()
|
||||
{
|
||||
assert(g_archive.curQuad);
|
||||
return g_archive.curQuad.level;
|
||||
}
|
||||
|
||||
// Get an alpha map belonging to this mesh
|
||||
AlphaInfo *getAlphaInfo(int num)
|
||||
{
|
||||
assert(num < alphaNum && num >= 0);
|
||||
assert(getLevel() == 1);
|
||||
AlphaInfo *res = cast(AlphaInfo*)g_archive.getRelSlice
|
||||
(alphaOffset, alphaNum*AlphaInfo.sizeof);
|
||||
res += num;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Get the size of the alpha textures (in pixels).
|
||||
int getAlphaSize()
|
||||
{ return g_archive.alphaSize; }
|
||||
|
||||
// Get the texture and material name to use for this mesh.
|
||||
char[] getTexName()
|
||||
{ return g_archive.getString(texName); }
|
||||
|
||||
float getTexScale()
|
||||
{ return g_archive.curQuad.texScale; }
|
||||
|
||||
char[] getBackgroundTex()
|
||||
{ return "_land_default.dds"; }
|
||||
}
|
||||
|
||||
struct ArchiveHeader
|
||||
{
|
||||
// "Magic" number to make sure we're actually reading an archive
|
||||
// file
|
||||
int magic;
|
||||
|
||||
// Total number of quads in the archive
|
||||
int quads;
|
||||
|
||||
// Level of the 'root' quad. There will only be one quad on this
|
||||
// level.
|
||||
int rootLevel;
|
||||
|
||||
// Size of the alpha maps, in pixels along one side.
|
||||
int alphaSize;
|
||||
|
||||
// Number of strings in the string table
|
||||
int stringNum;
|
||||
|
||||
// Size of the string buffer
|
||||
size_t stringSize;
|
||||
}
|
||||
|
||||
TerrainArchive g_archive;
|
||||
|
||||
// This class handles the cached terrain data.
|
||||
struct TerrainArchive
|
||||
{
|
||||
MeshInfo *curMesh;
|
||||
QuadInfo *curQuad;
|
||||
QuadInfo *rootQuad;
|
||||
|
||||
void openFile(char[] name)
|
||||
{
|
||||
mmf = new MmFile(name,
|
||||
MmFile.Mode.Read,
|
||||
0, null, pageSize);
|
||||
|
||||
// Read the index file first
|
||||
File ifile = new File(name ~ ".index");
|
||||
|
||||
ArchiveHeader head;
|
||||
ifile.readExact(&head, head.sizeof);
|
||||
|
||||
// Sanity check
|
||||
assert(head.magic == CACHE_MAGIC);
|
||||
assert(head.quads > 0 && head.quads < 8192);
|
||||
|
||||
// Store header info
|
||||
alphaSize = head.alphaSize;
|
||||
|
||||
// Read all the quads
|
||||
quadList = new QuadInfo[head.quads];
|
||||
ifile.readExact(quadList.ptr, head.quads*QuadInfo.sizeof);
|
||||
|
||||
// Create an index of all the quads
|
||||
foreach(int index, qn; quadList)
|
||||
{
|
||||
int x = qn.cellX;
|
||||
int y = qn.cellY;
|
||||
int l = qn.level;
|
||||
|
||||
assert(l >= 1);
|
||||
|
||||
quadMap[l][x][y] = index;
|
||||
|
||||
// Store the root quad
|
||||
if(l == head.rootLevel)
|
||||
{
|
||||
assert(rootQuad == null);
|
||||
rootQuad = &quadList[index];
|
||||
}
|
||||
else
|
||||
assert(l < head.rootLevel);
|
||||
}
|
||||
|
||||
// Make sure the root was set
|
||||
assert(rootQuad !is null);
|
||||
|
||||
// Next read the string table
|
||||
stringBuf = new char[head.stringSize];
|
||||
strings.length = head.stringNum;
|
||||
|
||||
// First read the main string buffer
|
||||
ifile.readExact(stringBuf.ptr, head.stringSize);
|
||||
|
||||
// Then read the string offsets
|
||||
int[] offsets = new int[head.stringNum];
|
||||
ifile.readExact(offsets.ptr, offsets.length*int.sizeof);
|
||||
|
||||
// Set up the string table
|
||||
char *strptr = stringBuf.ptr;
|
||||
foreach(int i, ref str; strings)
|
||||
{
|
||||
// toString(char*) returns the string up to the zero
|
||||
// terminator byte
|
||||
str = toString(strptr + offsets[i]);
|
||||
}
|
||||
delete offsets;
|
||||
|
||||
// Read the vertex buffer data
|
||||
int bufNum = head.rootLevel;
|
||||
assert(bufNum == 7);
|
||||
vertBufData.length = bufNum;
|
||||
indexBufData.length = bufNum;
|
||||
|
||||
// Fill the buffers. Start at level 1.
|
||||
for(int i=1;i<bufNum;i++)
|
||||
{
|
||||
int size;
|
||||
|
||||
// Vertex buffer
|
||||
ifile.read(size);
|
||||
vertBufData[i].length = size;
|
||||
ifile.readExact(vertBufData[i].ptr, size);
|
||||
|
||||
// Index buffer
|
||||
ifile.read(size);
|
||||
indexBufData[i].length = size;
|
||||
ifile.readExact(indexBufData[i].ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Get info about a given quad from the index.
|
||||
QuadInfo *getQuad(int X, int Y, int level)
|
||||
{
|
||||
int ind = quadMap[level][X][Y];
|
||||
QuadInfo *res = &quadList[ind];
|
||||
assert(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Maps the terrain and material info for a given quad into
|
||||
// memory. This is typically called right before the meshes are
|
||||
// created.
|
||||
void mapQuad(QuadInfo *info)
|
||||
{
|
||||
assert(info);
|
||||
|
||||
// Store the quad for later
|
||||
curQuad = info;
|
||||
|
||||
doMap(info.offset, info.size);
|
||||
}
|
||||
|
||||
// Get the info struct for a given segment. Remembers the MeshInfo
|
||||
// for all later calls.
|
||||
MeshInfo *getMeshInfo(int segNum)
|
||||
{
|
||||
assert(curQuad);
|
||||
assert(segNum < curQuad.meshNum);
|
||||
|
||||
// The mesh headers are at the beginning of the mapped segment.
|
||||
curMesh = cast(MeshInfo*) getRelSlice(0, MeshInfo.sizeof*curQuad.meshNum);
|
||||
curMesh += segNum;
|
||||
|
||||
return curMesh;
|
||||
}
|
||||
|
||||
float[] getVertexBuffer(int level)
|
||||
{
|
||||
assert(level>=1 && level<vertBufData.length);
|
||||
return vertBufData[level];
|
||||
}
|
||||
|
||||
ushort[] getIndexBuffer(int level)
|
||||
{
|
||||
assert(level>=1 && level<indexBufData.length);
|
||||
return indexBufData[level];
|
||||
}
|
||||
|
||||
private:
|
||||
// All quad headers (from the index) are stored in this array
|
||||
QuadInfo quadList[];
|
||||
|
||||
// A map of all quads. Contain indices to the above array. Indexed
|
||||
// by [level][X][Y].
|
||||
int[int][int][int] quadMap;
|
||||
|
||||
// These contain pregenerated mesh data that is common for all
|
||||
// meshes on a given level.
|
||||
float[][] vertBufData;
|
||||
ushort[][] indexBufData;
|
||||
|
||||
// Used for the mmapped file
|
||||
MmFile mmf;
|
||||
|
||||
ubyte mapped[];
|
||||
|
||||
// Stores the string table
|
||||
char[] stringBuf;
|
||||
char[][] strings;
|
||||
|
||||
// Texture size of the alpha maps.
|
||||
int alphaSize;
|
||||
|
||||
char[] getString(int index)
|
||||
{
|
||||
assert(index >= 0);
|
||||
assert(index < strings.length);
|
||||
|
||||
return strings[index];
|
||||
}
|
||||
|
||||
void doMap(size_t offset, size_t size)
|
||||
{
|
||||
assert(mmf !is null);
|
||||
assert(size);
|
||||
mapped = cast(ubyte[])mmf[offset..offset+size];
|
||||
assert(mapped.length == size);
|
||||
}
|
||||
|
||||
// Get a slice of a given buffer within the mapped window. The
|
||||
// offset is relative to the start of the window, and the size must
|
||||
// fit inside the window.
|
||||
ubyte[] getRelSlice(size_t offset, size_t size)
|
||||
{
|
||||
assert(mapped.length);
|
||||
|
||||
return mapped[offset..offset+size];
|
||||
}
|
||||
|
||||
// Copy a given buffer from the file. The buffer might be a
|
||||
// compressed stream, so it's important that the buffers are written
|
||||
// the same as they are read. (Ie. you can't write a buffer as one
|
||||
// operation and read it as two, or vice versa. Also, buffers cannot
|
||||
// overlap.) The offset is relative to the current mapped file
|
||||
// window.
|
||||
void copy(void *dst, size_t offset, size_t inSize)
|
||||
{
|
||||
ubyte source[] = getRelSlice(offset, inSize);
|
||||
|
||||
// Just copy it for now
|
||||
ubyte* dest = cast(ubyte*)dst;
|
||||
dest[0..source.length] = source[];
|
||||
}
|
||||
}
|
@ -0,0 +1,331 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2009 Nicolay Korslund
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (cachewriter.d) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
import terrain.archive;
|
||||
|
||||
import terrain.outbuffer;
|
||||
import std.stream;
|
||||
import monster.util.string;
|
||||
|
||||
// Helper structs
|
||||
struct AlphaHolder
|
||||
{
|
||||
AlphaInfo info;
|
||||
|
||||
// Actual pixel buffer
|
||||
ubyte[] buffer;
|
||||
}
|
||||
|
||||
struct MeshHolder
|
||||
{
|
||||
MeshInfo info;
|
||||
|
||||
// Actual buffers
|
||||
byte[] vertexBuffer;
|
||||
|
||||
// Texture name
|
||||
char[] texName;
|
||||
|
||||
// Alpha maps (if any)
|
||||
AlphaHolder alphas[];
|
||||
}
|
||||
|
||||
// A struct that gathers all the relevant quad data in one place.
|
||||
struct QuadHolder
|
||||
{
|
||||
QuadInfo info;
|
||||
|
||||
MeshHolder meshes[];
|
||||
}
|
||||
|
||||
struct CacheWriter
|
||||
{
|
||||
// Opens the main archive file for output
|
||||
void openFile(char[] fname)
|
||||
{
|
||||
mainFile = new File(fname, FileMode.OutNew);
|
||||
iname = fname ~ ".index";
|
||||
|
||||
buf = new OutBuffer;
|
||||
}
|
||||
|
||||
void setParams(int mxLev, int alphSize)
|
||||
{
|
||||
maxLevel = mxLev;
|
||||
alphaSize = alphSize;
|
||||
|
||||
vertBuf.length = maxLevel;
|
||||
indexBuf.length = maxLevel;
|
||||
}
|
||||
|
||||
// Closes the main archive file and writes the index.
|
||||
void finish()
|
||||
{
|
||||
mainFile.close();
|
||||
|
||||
// Write the index file
|
||||
scope File ofile = new File(iname, FileMode.OutNew);
|
||||
|
||||
// Header first
|
||||
ArchiveHeader head;
|
||||
head.magic = CACHE_MAGIC;
|
||||
head.quads = quadList.length;
|
||||
head.rootLevel = maxLevel;
|
||||
head.alphaSize = alphaSize;
|
||||
head.stringNum = stringList.length;
|
||||
head.stringSize = totalStringLength;
|
||||
ofile.writeExact(&head, head.sizeof);
|
||||
|
||||
// Write the quads
|
||||
foreach(qi; quadList)
|
||||
ofile.writeExact(&qi, qi.sizeof);
|
||||
|
||||
// String table next. We need to sort it in order of the indices
|
||||
// first.
|
||||
char[][] strVector;
|
||||
strVector.length = head.stringNum;
|
||||
|
||||
foreach(char[] key, int value; stringList)
|
||||
strVector[value] = key;
|
||||
|
||||
// Next, write the strings to file while we fill in the offset
|
||||
// list
|
||||
int[] offsets = new int[head.stringNum];
|
||||
|
||||
size_t curOffs = 0;
|
||||
for(int i=0; i<head.stringNum; i++)
|
||||
{
|
||||
// Add one byte for the zero terminator
|
||||
int len = strVector[i].length + 1;
|
||||
char *ptr = strVector[i].ptr;
|
||||
assert(ptr[len-1] == 0);
|
||||
|
||||
ofile.writeExact(ptr, len);
|
||||
|
||||
// Store the offset
|
||||
offsets[i] = curOffs;
|
||||
curOffs += len;
|
||||
}
|
||||
// At the end the offset should match the buffer size we set in
|
||||
// the header.
|
||||
assert(curOffs == head.stringSize);
|
||||
|
||||
// Finally, write the offset table itself
|
||||
ofile.writeExact(offsets.ptr, offsets.length * int.sizeof);
|
||||
|
||||
// Write the common vertex and index buffers
|
||||
for(int i=1;i<maxLevel;i++)
|
||||
{
|
||||
int size;
|
||||
void *ptr;
|
||||
|
||||
// Write vertex buffer
|
||||
ptr = vertBuf[i].ptr;
|
||||
size = vertBuf[i].length;
|
||||
ofile.write(size);
|
||||
ofile.writeExact(ptr, size);
|
||||
|
||||
// Then the index buffer
|
||||
ptr = indexBuf[i].ptr;
|
||||
size = indexBuf[i].length;
|
||||
ofile.write(size);
|
||||
ofile.writeExact(ptr, size);
|
||||
|
||||
delete vertBuf[i];
|
||||
delete indexBuf[i];
|
||||
}
|
||||
|
||||
// Don't need these anymore
|
||||
delete offsets;
|
||||
delete strVector;
|
||||
delete quadList;
|
||||
delete vertBuf;
|
||||
delete indexBuf;
|
||||
delete buf;
|
||||
delete mainFile;
|
||||
}
|
||||
|
||||
// Add a common vertex buffer for a given level
|
||||
void addVertexBuffer(int level, void[] buf)
|
||||
{
|
||||
assert(vertBuf.length > level);
|
||||
vertBuf[level] = buf;
|
||||
}
|
||||
|
||||
// Add a common vertex buffer for a given level
|
||||
void addIndexBuffer(int level, void[] buf)
|
||||
{
|
||||
assert(indexBuf.length > level);
|
||||
indexBuf[level] = buf;
|
||||
}
|
||||
|
||||
// Write a finished quad to the archive file. All the offsets and
|
||||
// numbers in the *Info structs are filled in automatically based on
|
||||
// the additional data in the Holder structs.
|
||||
void writeQuad(ref QuadHolder qh)
|
||||
{
|
||||
// Make outbuffer a simple struct that uses a region and keeps
|
||||
// track of all the slices we allocate.
|
||||
OutBuffer buf;
|
||||
|
||||
// Write the MeshInfo's first
|
||||
int meshNum = qh.meshes.length;
|
||||
|
||||
MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum);
|
||||
|
||||
// Then write the mesh data in approximately the order it's read
|
||||
for(int i=0; i<meshNum; i++)
|
||||
{
|
||||
assert(meshes !is null);
|
||||
|
||||
auto mh = &qh.meshes[i];
|
||||
|
||||
// Copy the basic data first
|
||||
meshes[i] = mh.info;
|
||||
|
||||
// Set everything else except the offsets
|
||||
int alphaNum = mh.alphas.length;
|
||||
meshes[i].alphaNum = alphaNum;
|
||||
//meshes[i].texName = addString(mh.texName);
|
||||
|
||||
// Write the vertex buffer
|
||||
meshes[i].vertBufOffset = buf.size;
|
||||
meshes[i].vertBufSize = mh.vertexBuffer.length;
|
||||
writeBuf(mh.vertexBuffer);
|
||||
|
||||
// Next write the alpha maps, if any
|
||||
meshes[i].alphaOffset = buf.size;
|
||||
AlphaInfo ais[] = buf.write!(AlphaInfo)(alphaNum);
|
||||
|
||||
// Loop through the alpha maps
|
||||
foreach(int k, ref ai; ais)
|
||||
{
|
||||
AlphaHolder ah = mh.alphas[k];
|
||||
ai = ah.info;
|
||||
|
||||
// Write the alpha pixel buffer
|
||||
ai.bufOffset = buf.size;
|
||||
ai.bufSize = ah.buffer.length;
|
||||
writeBuf(ah.buffer);
|
||||
}
|
||||
}
|
||||
// Finally set up the QuadInfo itself
|
||||
QuadInfo qi;
|
||||
|
||||
// Basic info
|
||||
qi = qh.info;
|
||||
|
||||
// Derived info
|
||||
qi.meshNum = meshNum;
|
||||
qi.offset = fileOffset;
|
||||
qi.size = buf.size;
|
||||
|
||||
// The quad cache is done, write it to file
|
||||
buf.writeTo(mainFile);
|
||||
|
||||
// Update the main offset
|
||||
fileOffset += qi.size;
|
||||
|
||||
// Add the quad to the list. This list isn't written to the main
|
||||
// cache file, but to the index file.
|
||||
quadList ~= qi;
|
||||
}
|
||||
|
||||
// Add a texture name as a string. Will convert .tga file names to
|
||||
// .dds as a convenience. TODO: Use the resource system to do this,
|
||||
// it automatically searches for the dds variant.
|
||||
int addTexture(char[] orig)
|
||||
{
|
||||
if(orig.iEnds(".tga"))
|
||||
orig = orig[0..$-3] ~ "dds";
|
||||
return addString(orig);
|
||||
}
|
||||
|
||||
// Convert a string to an index
|
||||
int addString(char[] str)
|
||||
{
|
||||
// Do we already have the string?
|
||||
if(str in stringList)
|
||||
return stringList[str];
|
||||
|
||||
// Nope, insert it
|
||||
int index = stringList.length;
|
||||
stringList[str] = index;
|
||||
stringLookup[index] = str;
|
||||
|
||||
// Sum up the string lengths + 1 byte for the zero
|
||||
totalStringLength += str.length + 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
char[] getString(int index)
|
||||
{
|
||||
char[] res = stringLookup[index];
|
||||
assert(stringList[res] == index);
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
// Write the given block of memory to 'buf', possibly compressing
|
||||
// the data.
|
||||
void writeBuf(void[] ptr)
|
||||
{
|
||||
ulong size = ptr.length;
|
||||
|
||||
// Reserve the maximum bytes needed.
|
||||
void toPtr[] = buf.reserve(size);
|
||||
|
||||
// Store the data
|
||||
toPtr[] = ptr[];
|
||||
|
||||
// Add the result buffer
|
||||
buf.add(toPtr[0..size]);
|
||||
}
|
||||
|
||||
// Used for 'writing' to a changable memory buffer before writing to
|
||||
// file
|
||||
OutBuffer buf;
|
||||
|
||||
// Common vertex and index buffers for all quads. One buffer per
|
||||
// level.
|
||||
void[][] vertBuf;
|
||||
void[][] indexBuf;
|
||||
|
||||
// Variables that must be set during the gen phase
|
||||
int maxLevel;
|
||||
int alphaSize;
|
||||
|
||||
// Contains a unique index for each string
|
||||
int[char[]] stringList;
|
||||
char[][int] stringLookup;
|
||||
size_t totalStringLength;
|
||||
|
||||
// List of all quads
|
||||
QuadInfo[] quadList;
|
||||
|
||||
// Output file
|
||||
File mainFile;
|
||||
size_t fileOffset;
|
||||
|
||||
// Index file name
|
||||
char[] iname;
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2009 Nicolay Korslund
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (cpp_cachewriter.cpp) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
// Helper structs
|
||||
struct AlphaHolder
|
||||
{
|
||||
AlphaInfo info;
|
||||
|
||||
// Actual pixel buffer
|
||||
unsigned char *buffer;
|
||||
|
||||
// Texture name and alpha material name to use
|
||||
//std::string texName, alphaName;
|
||||
};
|
||||
|
||||
struct MeshHolder
|
||||
{
|
||||
MeshInfo info;
|
||||
|
||||
// Actual buffers
|
||||
char *vertexBuffer;
|
||||
|
||||
// Texture name
|
||||
std::string texName;
|
||||
|
||||
// Alpha maps (if any)
|
||||
std::vector<AlphaHolder> alphas;
|
||||
};
|
||||
|
||||
// A struct that gathers all the relevant quad data in one place.
|
||||
struct QuadHolder
|
||||
{
|
||||
QuadInfo info;
|
||||
|
||||
std::vector<MeshHolder> meshes;
|
||||
};
|
||||
|
||||
class CacheWriter
|
||||
{
|
||||
public:
|
||||
// Opens the main archive file for output
|
||||
void openFile(const std::string &fname)
|
||||
{
|
||||
mainFile.open(fname.c_str(), std::ios::binary);
|
||||
iname = fname + ".index";
|
||||
fileOffset = 0;
|
||||
totalStringLength = 0;
|
||||
}
|
||||
|
||||
void setParams(int mxLev, int alphSize)
|
||||
{
|
||||
maxLevel = mxLev;
|
||||
alphaSize = alphSize;
|
||||
|
||||
vertBufData.resize(maxLevel);
|
||||
indexBufData.resize(maxLevel);
|
||||
vertBufSize.resize(maxLevel);
|
||||
indexBufSize.resize(maxLevel);
|
||||
}
|
||||
|
||||
// Closes the main archive file and writes the index.
|
||||
void finish()
|
||||
{
|
||||
mainFile.close();
|
||||
|
||||
// Write the index file
|
||||
std::ofstream ofile(iname.c_str(), std::ios::binary);
|
||||
|
||||
// Header first
|
||||
ArchiveHeader head;
|
||||
head.magic = CACHE_MAGIC;
|
||||
head.quads = quadList.size();
|
||||
head.rootLevel = maxLevel;
|
||||
head.alphaSize = alphaSize;
|
||||
head.stringNum = stringList.size();
|
||||
head.stringSize = totalStringLength;
|
||||
ofile.write((char*)&head, sizeof(head));
|
||||
|
||||
// Write the quads
|
||||
for(QuadList::iterator it = quadList.begin();
|
||||
it != quadList.end(); it++)
|
||||
{
|
||||
QuadInfo qi = *it;
|
||||
ofile.write((char*)&qi, sizeof(QuadInfo));
|
||||
}
|
||||
|
||||
// String table next. We need to sort it in order of the indices
|
||||
// first.
|
||||
std::vector<std::string> strVector;
|
||||
strVector.resize(head.stringNum);
|
||||
|
||||
for(StringList::iterator it = stringList.begin();
|
||||
it != stringList.end(); it++)
|
||||
{
|
||||
strVector[it->second] = it->first;
|
||||
}
|
||||
|
||||
// Next, write the strings to file while we fill inn the offset
|
||||
// list
|
||||
std::vector<int> offsets;
|
||||
offsets.resize(head.stringNum);
|
||||
size_t curOffs = 0;
|
||||
|
||||
for(int i=0; i<head.stringNum; i++)
|
||||
{
|
||||
// Add one byte for the zero terminator
|
||||
int len = strVector[i].length() + 1;
|
||||
const char *ptr = strVector[i].c_str();
|
||||
assert(ptr[len-1] == 0);
|
||||
|
||||
ofile.write(ptr, len);
|
||||
|
||||
// Store the offset
|
||||
offsets[i] = curOffs;
|
||||
curOffs += len;
|
||||
}
|
||||
// At the end the offset should match the buffer size we set in
|
||||
// the header.
|
||||
assert(curOffs == head.stringSize);
|
||||
|
||||
// Finally, write the offset table itself
|
||||
for(int i=0; i<head.stringNum; i++)
|
||||
{
|
||||
int offs = offsets[i];
|
||||
ofile.write((char*)&offs, sizeof(int));
|
||||
}
|
||||
|
||||
for(int i=1;i<maxLevel;i++)
|
||||
{
|
||||
int size;
|
||||
void *ptr;
|
||||
|
||||
// Write vertex buffer
|
||||
size = vertBufSize[i];
|
||||
ptr = vertBufData[i];
|
||||
ofile.write((char*)&size, sizeof(int));
|
||||
ofile.write((char*)ptr, size);
|
||||
|
||||
// Then the index buffer
|
||||
size = indexBufSize[i];
|
||||
ptr = indexBufData[i];
|
||||
ofile.write((char*)&size, sizeof(int));
|
||||
ofile.write((char*)ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a common vertex buffer for a given level
|
||||
void addVertexBuffer(int level, void *ptr, int size)
|
||||
{
|
||||
assert(vertBufData.size() > level);
|
||||
|
||||
vertBufData[level] = ptr;
|
||||
vertBufSize[level] = size;
|
||||
}
|
||||
|
||||
// Add a common vertex buffer for a given level
|
||||
void addIndexBuffer(int level, void *ptr, int size)
|
||||
{
|
||||
assert(indexBufData.size() > level);
|
||||
|
||||
indexBufData[level] = ptr;
|
||||
indexBufSize[level] = size;
|
||||
}
|
||||
|
||||
// Write a finished quad to the archive file. All the offsets and
|
||||
// numbers in the *Info structs are filled in automatically based on
|
||||
// the additional data in the Holder structs.
|
||||
void writeQuad(const QuadHolder &qh)
|
||||
{
|
||||
TRACE("writeQuad");
|
||||
|
||||
// See util/outbuffer.h
|
||||
OutBuffer buf;
|
||||
|
||||
// Write the MeshInfo's first
|
||||
int meshNum = qh.meshes.size();
|
||||
|
||||
MeshInfo *meshes = buf.write<MeshInfo>(meshNum);
|
||||
|
||||
// Then write the mesh data in approximately the order it's read
|
||||
for(int i=0; i<meshNum; i++)
|
||||
{
|
||||
assert(meshes != NULL);
|
||||
|
||||
const MeshHolder &mh = qh.meshes[i];
|
||||
|
||||
// Copy the basic data first
|
||||
meshes[i] = mh.info;
|
||||
|
||||
// Set everything else except the offsets
|
||||
int alphaNum = mh.alphas.size();
|
||||
meshes[i].alphaNum = alphaNum;
|
||||
//meshes[i].texName = addString(mh.texName);
|
||||
|
||||
// Write the vertex buffer
|
||||
meshes[i].vertBufOffset = buf.size();
|
||||
writeBuf(buf, mh.vertexBuffer, meshes[i].vertBufSize);
|
||||
|
||||
// Next write the alpha maps, if any
|
||||
meshes[i].alphaOffset = buf.size();
|
||||
AlphaInfo *ai = buf.write<AlphaInfo>(alphaNum);
|
||||
|
||||
// Loop through the alpha maps
|
||||
for(int k=0; k<alphaNum; k++)
|
||||
{
|
||||
AlphaHolder ah = mh.alphas[k];
|
||||
ai[k] = ah.info;
|
||||
|
||||
// Convert the strings
|
||||
// KILLME
|
||||
//ai[k].texName = addString(ah.texName);
|
||||
//ai[k].alphaName = addString(ah.alphaName);
|
||||
|
||||
// Write the alpha pixel buffer
|
||||
ai[k].bufOffset = buf.size();
|
||||
writeBuf(buf, ah.buffer, ai[k].bufSize);
|
||||
}
|
||||
}
|
||||
// The quad cache is done, write it to file
|
||||
mainFile << buf;
|
||||
|
||||
// Finally set up the QuadInfo itself
|
||||
QuadInfo qi;
|
||||
|
||||
// Basic info
|
||||
qi = qh.info;
|
||||
|
||||
// Derived info
|
||||
qi.meshNum = meshNum;
|
||||
qi.size = buf.size();
|
||||
qi.offset = fileOffset;
|
||||
|
||||
// Update the main offset
|
||||
fileOffset += qi.size;
|
||||
|
||||
// Add the quad to the index list
|
||||
quadList.push_back(qi);
|
||||
|
||||
std::cout << "end\n";
|
||||
}
|
||||
|
||||
// Add a texture name as a string. Will convert .tga file names to
|
||||
// .dds as a convenience
|
||||
int addTexture(const std::string &orig)
|
||||
{
|
||||
size_t d = orig.find_last_of(".") + 1;
|
||||
return addString(orig.substr(0, d) + "dds");
|
||||
}
|
||||
|
||||
// Convert a string to an index
|
||||
int addString(const std::string &str)
|
||||
{
|
||||
// Do we already have the string?
|
||||
StringList::iterator it = stringList.find(str);
|
||||
if(it != stringList.end())
|
||||
return it->second;
|
||||
|
||||
// Nope, insert it
|
||||
int index = stringList.size();
|
||||
stringList[str] = index;
|
||||
stringLookup[index] = str;
|
||||
|
||||
// Sum up the string lengths + 1 byte for the zero
|
||||
totalStringLength += str.length() + 1;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
const std::string &getString(int index)
|
||||
{
|
||||
const std::string &res = stringLookup[index];
|
||||
assert(stringList[res] == index);
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
// Write the given block of memory to 'buf', possibly compressing
|
||||
// the data.
|
||||
void writeBuf(OutBuffer &buf, const void *ptr, size_t size)
|
||||
{
|
||||
// Reserve the maximum bytes needed.
|
||||
void *toPtr = buf.reserve(size);
|
||||
|
||||
// Store the data
|
||||
memcpy(toPtr, ptr, size);
|
||||
|
||||
// Add the actual number of bytes stored
|
||||
buf.add(size);
|
||||
}
|
||||
|
||||
std::vector<void*> vertBufData;
|
||||
std::vector<void*> indexBufData;
|
||||
std::vector<int> vertBufSize;
|
||||
std::vector<int> indexBufSize;
|
||||
|
||||
// Variables that must be set during the gen phase
|
||||
int maxLevel;
|
||||
int alphaSize;
|
||||
|
||||
// Contains a unique index for each string
|
||||
typedef std::map<std::string, int> StringList;
|
||||
StringList stringList;
|
||||
std::map<int, std::string> stringLookup;
|
||||
size_t totalStringLength;
|
||||
|
||||
// List of all quads
|
||||
typedef std::list<QuadInfo> QuadList;
|
||||
QuadList quadList;
|
||||
|
||||
// Output file
|
||||
std::ofstream mainFile;
|
||||
size_t fileOffset;
|
||||
|
||||
// Index file name
|
||||
std::string iname;
|
||||
};
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Jacob Essex 2009
|
||||
|
||||
This file is part of MWLand.
|
||||
|
||||
MWLand 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MWLand 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 MWLand. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief utility class for holding two values
|
||||
*
|
||||
* This is mainly used for querying the position of the quad.
|
||||
* Each quad has a center position, which we can use as a unique identifier
|
||||
*/
|
||||
template<class T>
|
||||
struct Point2 {
|
||||
T x, y; //held values.
|
||||
|
||||
inline Point2() {}
|
||||
inline Point2(T ix, T iy) {
|
||||
x = ix;
|
||||
y = iy;
|
||||
}
|
||||
inline Point2(const Point2<T>& i) {
|
||||
x = i.x;
|
||||
y = i.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief comparison operator. Although not used directly, this
|
||||
* class is used in std::map a lot, which used the < operator
|
||||
*/
|
||||
|
||||
inline bool operator<(const Point2<T>& rhs) const{
|
||||
return ( x < rhs.x || !( rhs.x < x) && y < rhs.y );
|
||||
}
|
||||
|
||||
inline Point2 operator + (const Point2<T>& rhs) {
|
||||
return Point2(x + rhs.x, y + rhs.y);
|
||||
}
|
||||
};
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2009 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (generator.d) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
// This module is responsible for generating the cache files.
|
||||
module terrain.generator;
|
||||
|
||||
import std.stdio;
|
||||
import std.file;
|
||||
import monster.util.string;
|
||||
|
||||
char[] cacheDir = "cache/terrain/";
|
||||
|
||||
void generate()
|
||||
{
|
||||
makePath(cacheDir);
|
||||
terr_setCacheDir(cacheDir.ptr);
|
||||
}
|
||||
|
||||
// Move elsewhere, make part of the general cache system later
|
||||
void makeDir(char[] pt)
|
||||
{
|
||||
if(exists(pt))
|
||||
{
|
||||
if(!isdir(pt))
|
||||
fail(pt ~ " is not a directory");
|
||||
}
|
||||
else
|
||||
mkdir(pt);
|
||||
}
|
||||
|
||||
void fail(char[] msg)
|
||||
{
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
void makePath(char[] pt)
|
||||
{
|
||||
assert(!pt.begins("/"));
|
||||
foreach(int i, char c; pt)
|
||||
if(c == '/')
|
||||
makeDir(pt[0..i]);
|
||||
|
||||
if(!pt.ends("/"))
|
||||
makeDir(pt);
|
||||
}
|
||||
|
||||
extern(C):
|
||||
void terr_setCacheDir(char *dir);
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2009 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (outbuffer.d) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
*/
|
||||
|
||||
/*
|
||||
This files provides a simple buffer class used for writing the cache
|
||||
files. It lets you 'write' data to a growing memory buffer and
|
||||
allows you to change the written data after the fact (since it's
|
||||
retained in memory.) When you're done, you can write the entire
|
||||
buffer to a stream in one operation.
|
||||
*/
|
||||
|
||||
import util.regions;
|
||||
import std.stream;
|
||||
|
||||
class OutBuffer
|
||||
{
|
||||
private:
|
||||
RegionManager reg;
|
||||
long used;
|
||||
void[][] buffers;
|
||||
|
||||
public:
|
||||
this()
|
||||
{
|
||||
reg = new RegionManager("Outbuf", 200*1024);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if(buffers.length)
|
||||
delete buffers;
|
||||
|
||||
reg.freeAll();
|
||||
used = 0;
|
||||
buffers = null;
|
||||
}
|
||||
|
||||
// Write everyting to a stream as one buffer
|
||||
void writeTo(Stream str)
|
||||
{
|
||||
foreach(void[] v; buffers)
|
||||
str.writeExact(v.ptr, v.length);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
// Get a pointer to a new block at least 'bytes' large, but don't
|
||||
// add it to the list.
|
||||
void[] reserve(size_t bytes)
|
||||
{ return reg.allocate(bytes); }
|
||||
|
||||
// Get a new block which is 'bytes' size large.
|
||||
void[] add(size_t bytes)
|
||||
{
|
||||
void[] p = reserve(bytes);
|
||||
add(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
// Add an existing block to the write list
|
||||
void add(void[] p)
|
||||
{
|
||||
buffers ~= p;
|
||||
used += p.length;
|
||||
}
|
||||
|
||||
T[] write(T)(size_t num)
|
||||
{
|
||||
return cast(T[])add(num * T.sizeof);
|
||||
}
|
||||
|
||||
size_t size() { return used; }
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2009 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (c_mmfile.d) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
This file provides a simple interface to memory mapped files
|
||||
through C functions. Since D's MmFile is superior in terms of
|
||||
usability and platform independence, we use it even for C++ code.
|
||||
*/
|
||||
|
||||
module util.c_mmfile;
|
||||
|
||||
import std.mmfile;
|
||||
import std.string;
|
||||
|
||||
version(Windows)
|
||||
static int pageSize = 64*1024;
|
||||
else
|
||||
static int pageSize = 4*1024;
|
||||
|
||||
// List of all MMFs in existence, to keep the GC from killing them
|
||||
int[MmFile] mf_list;
|
||||
|
||||
extern(C):
|
||||
|
||||
// Open a new memory mapped file
|
||||
MmFile mmf_open(char *fileName)
|
||||
{
|
||||
auto mmf = new MmFile(toString(fileName),
|
||||
MmFile.Mode.Read,
|
||||
0, null, pageSize);
|
||||
mf_list[mmf] = 1;
|
||||
return mmf;
|
||||
}
|
||||
|
||||
// Close a file. Do not use the handle after calling this function, as
|
||||
// the object gets deleted
|
||||
void mmf_close(MmFile mmf)
|
||||
{
|
||||
mf_list.remove(mmf);
|
||||
delete mmf;
|
||||
}
|
||||
|
||||
// Map a region of the file. Do NOT attempt to access several regions
|
||||
// at once. Map will almost always unmap the current mapping (thus
|
||||
// making all current pointers invalid) when a new map is requested.
|
||||
void* mmf_map(MmFile mmf, ulong offset, ulong size)
|
||||
{
|
||||
return mmf[offset..offset+size].ptr;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
|
||||
typedef void* D_MmFile;
|
||||
|
||||
// These functions are implemented in util/c_mmfile.d
|
||||
extern "C"
|
||||
{
|
||||
// Open a new memory mapped file
|
||||
D_MmFile mmf_open(const char *fileName);
|
||||
|
||||
// Close a file. Do not use the handle after calling this function,
|
||||
// as the object gets deleted
|
||||
void mmf_close(D_MmFile mmf);
|
||||
|
||||
// Map a region of the file. Do NOT attempt to access several
|
||||
// regions at once. Map will almost always unmap the current mapping
|
||||
// (thus making all current pointers invalid) when a new map is
|
||||
// requested.
|
||||
void* mmf_map(D_MmFile mmf, int64_t offset, int64_t size);
|
||||
}
|
||||
|
||||
// This struct allows you to open, read and close a memory mapped
|
||||
// file. It uses the D MmFile class to achieve platform independence
|
||||
// and an abstract interface.
|
||||
struct MmFile
|
||||
{
|
||||
MmFile(const std::string &file)
|
||||
{
|
||||
mmf = mmf_open(file.c_str());
|
||||
}
|
||||
|
||||
~MmFile()
|
||||
{
|
||||
mmf_close(mmf);
|
||||
}
|
||||
|
||||
void *map(int64_t offset, int64_t size)
|
||||
{
|
||||
return mmf_map(mmf, offset, size);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
D_MmFile mmf;
|
||||
};
|
@ -1,129 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2009 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (outbuffer.h) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program 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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
*/
|
||||
|
||||
/*
|
||||
This files provides a simple buffer class used for writing the cache
|
||||
files. It lets you 'write' data to a growing memory buffer and
|
||||
allows you to change the written data after the fact (since it's
|
||||
retained in memory.) When you're done, you can write the entire
|
||||
buffer to a stream in one operation.
|
||||
*/
|
||||
|
||||
// This is sort of like a mini-version of the Region class in
|
||||
// D. FIXME: And it doesn't need to be. Rewrite this to add buffers of
|
||||
// the exact size requested instead of filling a buffer of predefined
|
||||
// size.
|
||||
class OutBuffer
|
||||
{
|
||||
public:
|
||||
OutBuffer() : used(0), left(0), buffers(), sizes()
|
||||
{}
|
||||
|
||||
~OutBuffer()
|
||||
{
|
||||
deallocate();
|
||||
}
|
||||
|
||||
// Write everyting to a stream as one buffer
|
||||
void writeTo(std::ostream &str)
|
||||
{
|
||||
for(int i=0;i<buffers.size();i++)
|
||||
str.write((char*)buffers[i], sizes[i]);
|
||||
}
|
||||
|
||||
// Get a pointer to a new block at least 'bytes' large. Allocate a
|
||||
// new buffer if necessary.
|
||||
void *reserve(size_t bytes)
|
||||
{
|
||||
assert(bytes <= bufSize);
|
||||
|
||||
if(left >= bytes)
|
||||
return curPtr;
|
||||
|
||||
// Not enough space left. Allocate a new buffer.
|
||||
curPtr = (char*)malloc(bufSize);
|
||||
left = bufSize;
|
||||
|
||||
// Store the new buffer in the lists
|
||||
buffers.push_back(curPtr);
|
||||
sizes.push_back(0);
|
||||
|
||||
return curPtr;
|
||||
}
|
||||
|
||||
// Get a new block which is 'bytes' size large. The block will be
|
||||
// marked as 'used'.
|
||||
void *add(size_t bytes)
|
||||
{
|
||||
void *res = reserve(bytes);
|
||||
|
||||
if(bytes == 0)
|
||||
return res;
|
||||
|
||||
assert(left >= bytes);
|
||||
curPtr += bytes;
|
||||
left -= bytes;
|
||||
|
||||
// We keep a count of the total number of bytes used
|
||||
used += bytes;
|
||||
|
||||
// Keep a count for each buffer as well
|
||||
sizes[sizes.size()-1] += bytes;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* write(size_t num)
|
||||
{
|
||||
return (T*)add(num*sizeof(T));
|
||||
}
|
||||
|
||||
void deallocate()
|
||||
{
|
||||
for(int i=0;i<buffers.size();i++)
|
||||
free(buffers[i]);
|
||||
|
||||
buffers.clear();
|
||||
sizes.clear();
|
||||
|
||||
left = 0;
|
||||
used = 0;
|
||||
}
|
||||
|
||||
size_t size() { return used; }
|
||||
|
||||
private:
|
||||
std::vector<void*> buffers;
|
||||
std::vector<int> sizes;
|
||||
size_t used, left;
|
||||
char *curPtr;
|
||||
|
||||
static const size_t bufSize = 200*1024;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, OutBuffer& buf)
|
||||
{
|
||||
buf.writeTo(os);
|
||||
return os;
|
||||
}
|
||||
|
Loading…
Reference in New Issue