2

我正在用本机和托管 C++ 为 4GB+ 的大型存档文件(见下文)编写一个编辑器。

为了访问文件,我像任何理智的人一样使用文件映射(见下文)。这对于读取数据非常有用,但是在实际编辑存档时会出现问题。文件映射不允许在访问文件时调整文件大小,所以我不知道当用户想要在文件中插入新数据时我应该如何进行(这将超过文件的原始大小,当它被映射时。)

我应该每次都重新映射整个事情吗?那肯定会很慢。但是,我希望通过独占文件访问来保持编辑器的实时性,因为这大大简化了编程,并且不会让文件在被修改时被其他应用程序搞砸。我不想在编辑器上花费永恒的时间;它只是我正在从事的实际项目的一个简单的开发工具。

所以我想听听你们是如何处理类似情况的,还有哪些其他存档软件,尤其是其他游戏可以解决这个问题?

澄清:

  • 这不是文本文件,我正在编写特定的二进制存档文件格式。我的意思是在目录中包含许多其他文件的大文件。出于多种原因,自定义存档文件在游戏使用中非常常见。对于我的格式,我的目标是与Valve Software 的 GCF 格式类似(但更简单)的结构- 我本来会使用 GCF 格式,但不幸的是,尽管有很多很棒的实现,但该格式没有编辑器用于阅读它们,例如HLLib

  • 访问文件必须快速,因为它旨在存储游戏资源。所以它不是数据库。数据库文件将包含在其中,以及 GFX、SFX 等文件。

  • 这里所说的“文件映射”是 Windows 平台上的一种特定技术,它允许通过创建部分文件的“视图”来直接访问大文件,请参见此处:http: //msdn.microsoft.com/en-us /library/aa366556(VS.85).aspx - 这种技术允许最小的延迟和内存使用,并且是访问任何大文件的明智之举。所以这并不意味着将整个 4GB 文件读入内存,恰恰相反。

4

6 回答 6

2

“编辑软件”是什么意思?如果这是一个文本文件,在编写自己的之前,您是否尝试过现有的生产质量编辑器?如果是存储二进制数据的文件,您是否考虑过使用 RDBMS 并使用 SQL 语句操作其内容?

如果您绝对必须从头开始编写此内容,我不确定映射是否正确。映射一个巨大的文件会给你机器的VM系统带来很大的压力,除非整个文件有很多编辑操作,否则它的效率可能会落后于简单的读/写方案。更糟糕的是,正如您所说,您在扩展文件时遇到问题。

相反,维护文件数据的缓冲区窗口,用户可以修改这些数据。当用户决定保存文件时,依次遍历文件和已编辑的缓冲区以创建新的文件图像。如果您有磁盘空间,则更容易写入新文件(尤其是在缓冲区大小发生变化的情况下),否则在用新内容覆盖之前,您需要巧妙地预读现有数据。

或者,您可以记录编辑操作。当用户决定保存文件时,对日志进行拓扑排序并在现有文件上播放以创建新文件。

对于独占文件访问,请使用操作系统的文件锁定或实施应用程序级锁定(如果只有您的编辑器会接触这些文件)。依赖 mmap 的独占访问限制了您的实现选择。

于 2008-10-12T17:38:17.147 回答
2

映射文件是为了实际访问数据而创建的,但我认为您需要另一个表示文件结构的抽象。有多种方法可以做到这一点,但考虑将文件表示为“范围”序列。

从文件开始是一个单一的范围,相当于整个映射。如果用户随后开始编辑文件,您将在编辑点将单个范围一分为二,并插入一个包含用户已插入数据的新范围。修改和删除也会通过创建或修改这些范围来修改您的文件视图。

也许您可以检查其中一个开源编辑器的源代码——有很多可供选择,但找到一个足够简单的将是一个挑战。

于 2008-10-12T17:46:35.240 回答
1

这个问题没有简单的答案——我找了很长时间,但徒劳无功。您必须修改文件的大小,然后重新映射它。

于 2008-10-12T18:12:01.400 回答
1

我所做的是关闭视图句柄和 FileMapping 句柄,设置文件大小然后重新打开映射/视图句柄。

// Open memory mapped file    
HANDLE FileHandle = ::CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
size_t Size = ::GetFileSize(FileHandle, 0);
HANDLE MappingHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READWRITE, 0, Size, NULL);
void* ViewHandle = ::MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);

...

// increase size of file
UnmapViewOfFile(ViewHandle);
CloseHandle(MappingHandle);

Size += 1024;


LARGE_INTEGER offset;
offset.QuadPart = Size;

LARGE_INTEGER newpos;
SetFilePointerEx(FileHandle, offset, &newpos, FILE_BEGIN);
SetEndOfFile(FileHandle);

MappingHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READWRITE, 0, Size, NULL);
ViewHandle = ::MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);

上面的代码没有错误检查并且不处理 64 位大小,但这并不难修复。

于 2008-10-12T23:03:37.427 回答
1

映射对远程系统上的文件有一个基本问题。

在过去美好的 DOS 时代,有一个很好的编辑器,叫做 Norton Editor(ne.com .. 这是文件名,不是网站)。它可以加载任何大小的文件(我们说的是 640kb RAM 和 20 GB 硬盘,如果有的话)。

它过去只加载部分文件,巧妙地管理文件长搜索和按需加载

恕我直言,应该使用这种方法。

如果适当地隐藏在文件读写层下,它可能会非常透明。

于 2009-01-31T13:03:32.027 回答
0

我会在构建时从片段构建大文件。您让您的编辑器在通常的文件系统中处理普通的平面文件(适当时带有子目录等)。然后,您有一个编译步骤,将所有这些部分收集到您的存档文件格式中。

于 2009-04-20T15:44:02.850 回答