Beispiel #1
0
/* Wad2Archive::write
 * Writes the wad archive to a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool Wad2Archive::write(MemChunk& mc, bool update)
{
	// Determine directory offset & individual lump offsets
	uint32_t dir_offset = 12;
	ArchiveEntry* entry = NULL;
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		entry->exProp("Offset") = (int)dir_offset;
		dir_offset += entry->getSize();
	}

	// Clear/init MemChunk
	mc.clear();
	mc.seek(0, SEEK_SET);
	mc.reSize(dir_offset + numEntries() * 32);

	// Setup wad type
	char wad_type[4] = { 'W', 'A', 'D', '2' };
	if (wad3) wad_type[3] = '3';

	// Write the header
	uint32_t num_lumps = numEntries();
	mc.write(wad_type, 4);
	mc.write(&num_lumps, 4);
	mc.write(&dir_offset, 4);

	// Write the lumps
	for (uint32_t l = 0; l < num_lumps; l++)
	{
		entry = getEntry(l);
		mc.write(entry->getData(), entry->getSize());
	}

	// Write the directory
	for (uint32_t l = 0; l < num_lumps; l++)
	{
		entry = getEntry(l);

		// Setup directory entry
		wad2entry_t info;
		memset(info.name, 0, 16);
		memcpy(info.name, CHR(entry->getName()), entry->getName().Len());
		info.cmprs = (bool)entry->exProp("W2Comp");
		info.dsize = entry->getSize();
		info.size = entry->getSize();
		info.offset = (int)entry->exProp("Offset");
		info.type = (int)entry->exProp("W2Type");

		// Write it
		mc.write(&info, 32);

		if (update)
			entry->setState(0);
	}

	return true;
}
Beispiel #2
0
/* MapEditorWindow::loadMapScripts
 * Loads any scripts from [map] into the script editor
 *******************************************************************/
void MapEditorWindow::loadMapScripts(Archive::mapdesc_t map)
{
	// Don't bother if no scripting language specified
	if (theGameConfiguration->scriptLanguage().IsEmpty())
	{
		// Hide script editor
		wxAuiManager* m_mgr = wxAuiManager::GetManager(this);
		wxAuiPaneInfo& p_inf = m_mgr->GetPane("script_editor");
		p_inf.Show(false);
		m_mgr->Update();
		return;
	}

	// Don't bother if new map
	if (!map.head)
	{
		panel_script_editor->openScripts(NULL, NULL);
		return;
	}

	// Check for pk3 map
	if (map.archive)
	{
		WadArchive* wad = new WadArchive();
		wad->open(map.head->getMCData());
		vector<Archive::mapdesc_t> maps = wad->detectMaps();
		if (!maps.empty())
		{
			loadMapScripts(maps[0]);
			wad->close();
			delete wad;
			return;
		}
	}

	// Go through map entries
	ArchiveEntry* entry = map.head->nextEntry();
	ArchiveEntry* scripts = NULL;
	ArchiveEntry* compiled = NULL;
	while (entry && entry != map.end->nextEntry())
	{
		// Check for SCRIPTS/BEHAVIOR
		if (theGameConfiguration->scriptLanguage() == "acs_hexen" ||
		        theGameConfiguration->scriptLanguage() == "acs_zdoom")
		{
			if (S_CMPNOCASE(entry->getName(), "SCRIPTS"))
				scripts = entry;
			if (S_CMPNOCASE(entry->getName(), "BEHAVIOR"))
				compiled = entry;
		}

		// Next entry
		entry = entry->nextEntry();
	}

	// Open scripts/compiled if found
	panel_script_editor->openScripts(scripts, compiled);
}
Beispiel #3
0
/* GobArchive::write
 * Writes the gob archive to a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool GobArchive::write(MemChunk& mc, bool update)
{
	// Determine directory offset & individual lump offsets
	uint32_t dir_offset = 8;
	ArchiveEntry* entry = NULL;
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		setEntryOffset(entry, dir_offset);
		dir_offset += entry->getSize();
	}

	// Clear/init MemChunk
	mc.clear();
	mc.seek(0, SEEK_SET);
	mc.reSize(dir_offset + 4 + numEntries() * 21);

	// Write the header
	uint32_t num_lumps = wxINT32_SWAP_ON_BE(numEntries());
	dir_offset = wxINT32_SWAP_ON_BE(dir_offset);
	char header[4] = { 'G', 'O', 'B', 0xA };
	mc.write(header, 4);
	mc.write(&dir_offset, 4);

	// Write the lumps
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		mc.write(entry->getData(), entry->getSize());
	}

	// Write the directory
	mc.write(&num_lumps, 4);
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
		long offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry));
		long size = wxINT32_SWAP_ON_BE(entry->getSize());

		for (size_t c = 0; c < entry->getName().length() && c < 13; c++)
			name[c] = entry->getName()[c];

		mc.write(&offset, 4);
		mc.write(&size, 4);
		mc.write(name, 13);

		if (update)
		{
			entry->setState(0);
			entry->exProp("Offset") = (int)offset;
		}
	}

	return true;
}
Beispiel #4
0
// ----------------------------------------------------------------------------
// ArchiveEntryList::getItemText
//
// Called when the widget requests the text for [item] at [column]
// ----------------------------------------------------------------------------
string ArchiveEntryList::getItemText(long item, long column, long index) const
{
	// Get entry
	ArchiveEntry* entry = getEntry(index, false);

	// Check entry
	if (!entry)
		return "INVALID INDEX";

	// Determine what column we want
	int col = columnType(column);

	if (col == 0)
		return entry->getName();	// Name column
	else if (col == 1)
	{
		// Size column
		if (entry->getType() == EntryType::folderType())
		{
			// Entry is a folder, return the number of entries+subdirectories in it
			ArchiveTreeNode* dir = nullptr;

			// Get selected directory
			if (entry == entry_dir_back)
				dir = (ArchiveTreeNode*)current_dir->getParent();	// If it's the 'back directory', get the current dir's parent
			else
				dir = archive->getDir(entry->getName(), current_dir);

			// If it's null, return error
			if (!dir)
				return "INVALID DIRECTORY";

			// Return the number of items in the directory
			return S_FMT("%d entries", dir->numEntries() + dir->nChildren());
		}
		else
			return entry->getSizeString();	// Not a folder, just return the normal size string
	}
	else if (col == 2)
		return entry->getTypeString();	// Type column
	else if (col == 3)
	{
		// Index column
		if (entry->getType() == EntryType::folderType())
			return "";
		else
			return S_FMT("%d", entry->getParentDir()->entryIndex(entry));
	}
	else
		return "INVALID COLUMN";		// Invalid column
}
Beispiel #5
0
/* HogArchive::write
 * Writes the hog archive to a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool HogArchive::write(MemChunk& mc, bool update)
{
	// Determine individual lump offsets
	uint32_t offset = 3;
	ArchiveEntry* entry = NULL;
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		offset += 17;
		entry = getEntry(l);
		setEntryOffset(entry, offset);
		if (update)
		{
			entry->setState(0);
			entry->exProp("Offset") = (int)offset;
		}
		offset += entry->getSize();
	}

	// Clear/init MemChunk
	mc.clear();
	mc.seek(0, SEEK_SET);
	mc.reSize(offset);

	// Write the header
	char header[3] = { 'D', 'H', 'F' };
	mc.write(header, 3);

	// Write the lumps
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		mc.write(entry->getData(), entry->getSize());
	}

	// Write the directory
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		char name[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
		long size = wxINT32_SWAP_ON_BE(entry->getSize());

		for (size_t c = 0; c < entry->getName().length() && c < 13; c++)
			name[c] = entry->getName()[c];

		mc.write(name, 13);
		mc.write(&size, 4);
		mc.write(entry->getData(), entry->getSize());
	}

	return true;
}
Beispiel #6
0
// ----------------------------------------------------------------------------
// ArchiveEntryList::getSelectedDirectories
//
// Returns a vector of all currently selected directories
// ----------------------------------------------------------------------------
vector<ArchiveTreeNode*> ArchiveEntryList::getSelectedDirectories()
{
	vector<ArchiveTreeNode*> ret;

	// Get all selected items
	vector<long> selection = getSelection();

	// Go through the selection
	for (size_t a = 0; a < selection.size(); a++)
	{
		ArchiveEntry* entry = getEntry(selection[a]);

		// If the selected entry is the 'back folder', ignore it
		if (entry == entry_dir_back)
			continue;
		else if (entry->getType() == EntryType::folderType())
		{
			// If the entry is a folder type, get its ArchiveTreeNode counterpart
			ArchiveTreeNode* dir = archive->getDir(entry->getName(), current_dir);

			// Add it to the return list
			if (dir)
				ret.push_back(dir);
		}
	}

	return ret;
}
Beispiel #7
0
void importEditorImages(MapTexHashMap& map, ArchiveTreeNode* dir, string path)
{
	SImage image;

	// Go through entries
	for (unsigned a = 0; a < dir->numEntries(); a++)
	{
		ArchiveEntry* entry = dir->getEntry(a);

		// Load entry to image
		if (image.open(entry->getMCData()))
		{
			// Create texture in hashmap
			string name = path + entry->getName(true);
			//wxLogMessage("Loading editor texture %s", CHR(name));
			map_tex_t& mtex = map[name];
			mtex.texture = new GLTexture(false);
			mtex.texture->setFilter(GLTexture::MIPMAP);
			mtex.texture->loadImage(&image);
		}
	}

	// Go through subdirs
	for (unsigned a = 0; a < dir->nChildren(); a++)
	{
		ArchiveTreeNode* subdir = (ArchiveTreeNode*)dir->getChild(a);
		importEditorImages(map, subdir, path + subdir->getName() + "/");
	}
}
Beispiel #8
0
/* TextureClipboardItem::TextureClipboardItem
 * TextureClipboardItem class constructor
 *******************************************************************/
TextureClipboardItem::TextureClipboardItem(CTexture* texture, Archive* parent) : ClipboardItem(CLIPBOARD_COMPOSITE_TEXTURE)
{
    // Create/copy texture
    this->texture = new CTexture();
    this->texture->copyTexture(texture);

    // Copy patch entries if possible
    for (unsigned a = 0; a < texture->nPatches(); a++)
    {
        ArchiveEntry* entry = texture->getPatch(a)->getPatchEntry(parent);

        // FIXME/TODO: Do something to handle patches that are defined
        // in TEXTURES rather than a discrete entry!
        if (entry == NULL)
            continue;

        // Don't copy patch if it has been already
        bool there = false;
        for (unsigned b = 0; b < patch_entries.size(); b++)
        {
            if (patch_entries[b]->getName() == entry->getName())
            {
                there = true;
                break;
            }
        }
        if (there) continue;

        // Copy patch entry
        if (entry)
            patch_entries.push_back(new ArchiveEntry(*entry));
    }
}
Beispiel #9
0
// ----------------------------------------------------------------------------
// ArchiveEntryList::onListItemActivated
//
// Called when a list item is 'activated' (double-click or enter)
// ----------------------------------------------------------------------------
void ArchiveEntryList::onListItemActivated(wxListEvent& e)
{
	// Get item entry
	ArchiveEntry* entry = getEntry(e.GetIndex());

	// Do nothing if NULL (shouldn't be)
	if (!entry)
		return;

	// If it's a folder, open it
	if (entry->getType() == EntryType::folderType())
	{
		// Get directory to open
		ArchiveTreeNode* dir = nullptr;
		if (entry == entry_dir_back)
			dir = (ArchiveTreeNode*)current_dir->getParent();	// 'Back directory' entry, open current dir's parent
		else
			dir = archive->getDir(entry->getName(), current_dir);

		// Check it exists (really should)
		if (!dir)
		{
			LOG_MESSAGE(1, "Error: Trying to open nonexistant directory");
			return;
		}

		// Set current dir
		setDir(dir);
	}
	else
		e.Skip();
}
Beispiel #10
0
/* DatArchive::updateNamespaces
 * Updates the namespace list
 *******************************************************************/
void DatArchive::updateNamespaces()
{
	// Clear current namespace info
	sprites[0] = sprites[1] = flats[0] = flats[1] = walls[0] = walls[1] = -1;

	// Go through all entries
	for (unsigned a = 0; a < numEntries(); a++)
	{
		ArchiveEntry* entry = getRoot()->getEntry(a);

		// Check for markers
		if (!entry->getName().Cmp("startflats"))
			flats[0] = a;
		if (!entry->getName().Cmp("endflats"))
			flats[1] = a;
		if (!entry->getName().Cmp("startsprites"))
			sprites[0] = a;
		if (!entry->getName().Cmp("endmonsters"))
			sprites[1] = a;
		if (!entry->getName().Cmp("startwalls"))
			walls[0] = a;
		if (!entry->getName().Cmp("endwalls"))
			walls[1] = a;
	}
}
Beispiel #11
0
// ----------------------------------------------------------------------------
// ArchiveEntryList::entrySize
//
// Returns either the size of the entry at [index], or if it is a folder, the
// number of entries+subfolders within it
// ----------------------------------------------------------------------------
int ArchiveEntryList::entrySize(long index)
{
	ArchiveEntry* entry = getEntry(index, false);
	if (entry->getType() == EntryType::folderType())
	{
		ArchiveTreeNode* dir = archive->getDir(entry->getName(), current_dir);
		if (dir)
			return dir->numEntries() + dir->nChildren();
		else
			return 0;
	}
	else
		return entry->getSize();
}
Beispiel #12
0
/* ZipArchive::detectMaps
 * Detects all the maps in the archive and returns a vector of
 * information about them.
 *******************************************************************/
vector<Archive::mapdesc_t> ZipArchive::detectMaps()
{
	vector<mapdesc_t> ret;

	// Get the maps directory
	ArchiveTreeNode* mapdir = getDir("maps");
	if (!mapdir)
		return ret;

	// Go through entries in map dir
	for (unsigned a = 0; a < mapdir->numEntries(); a++)
	{
		ArchiveEntry* entry = mapdir->getEntry(a);

		// Maps can only be wad archives
		if (entry->getType()->getFormat() != "archive_wad")
			continue;

		// Detect map format (probably kinda slow but whatever, no better way to do it really)
		int format = MAP_UNKNOWN;
		Archive* tempwad = new WadArchive();
		tempwad->open(entry);
		vector<mapdesc_t> emaps = tempwad->detectMaps();
		if (emaps.size() > 0)
			format = emaps[0].format;
		delete tempwad;

		// Add map description
		mapdesc_t md;
		md.head = entry;
		md.end = entry;
		md.archive = true;
		md.name = entry->getName(true).Upper();
		md.format = format;
		ret.push_back(md);
	}

	return ret;
}
Beispiel #13
0
/* GZipArchive::findFirst
 * Returns the entry if it matches the search criteria in [options],
 * or NULL otherwise
 *******************************************************************/
ArchiveEntry* GZipArchive::findFirst(search_options_t& options)
{
	// Init search variables
	options.match_name = options.match_name.Lower();
	ArchiveEntry* entry = getEntry(0);
	if (entry == NULL)
		return entry;

	// Check type
	if (options.match_type)
	{
		if (entry->getType() == EntryType::unknownType())
		{
			if (!options.match_type->isThisType(entry))
			{
				return NULL;
			}
		}
		else if (options.match_type != entry->getType())
		{
			return NULL;
		}
	}

	// Check name
	if (!options.match_name.IsEmpty())
	{
		if (!options.match_name.Matches(entry->getName().Lower()))
		{
			return NULL;
		}
	}

	// Entry passed all checks so far, so we found a match
	return entry;
}
Beispiel #14
0
/* loadIcons
 * Loads all icons from slade.pk3 (in the icons/ dir)
 *******************************************************************/
bool loadIcons()
{
	string tempfile = appPath("sladetemp", DIR_TEMP);

	// Get slade.pk3
	Archive* res_archive = theArchiveManager->programResourceArchive();

	// Do nothing if it doesn't exist
	if (!res_archive)
		return false;

	// Get the icons directory of the archive
	ArchiveTreeNode* dir_icons = res_archive->getDir("icons/");

	// Go through each entry in the directory
	for (size_t a = 0; a < dir_icons->numEntries(false); a++)
	{
		ArchiveEntry* entry = dir_icons->getEntry(a);

		// Export entry data to a temporary file
		entry->exportFile(tempfile);

		// Create / setup icon
		icon_t n_icon;
		n_icon.image.LoadFile(tempfile);	// Load image from temp file
		n_icon.name = entry->getName(true);	// Set icon name

		// Add the icon
		icons.push_back(n_icon);

		// Delete the temporary file
		wxRemoveFile(tempfile);
	}

	return true;
}
Beispiel #15
0
/* WadArchive::getMapInfo
 * Returns the mapdesc_t information about the map beginning at
 * [maphead]. If [maphead] is not really a map header entry, an
 * invalid mapdesc_t will be returned (mapdesc_t::head == NULL)
 *******************************************************************/
Archive::mapdesc_t WadArchive::getMapInfo(ArchiveEntry* maphead)
{
	mapdesc_t map;

	if (!maphead)
		return map;

	// Check for embedded wads (e.g., Doom 64 maps)
	if (maphead->getType()->getFormat() == "archive_wad")
	{
		map.archive = true;
		map.head = maphead;
		map.end = maphead;
		map.name = maphead->getName();
		return map;
	}

	// Check for UDMF format map
	if (S_CMPNOCASE(maphead->nextEntry()->getName(), "TEXTMAP"))
	{
		// Get map info
		map.head = maphead;
		map.name = maphead->getName();
		map.format = MAP_UDMF;

		// All entries until we find ENDMAP
		ArchiveEntry* entry = maphead->nextEntry();
		while (true)
		{
			if (!entry || S_CMPNOCASE(entry->getName(), "ENDMAP"))
				break;

			// Check for unknown map lumps
			bool known = false;
			for (unsigned a = 0; a < NUMMAPLUMPS; a++)
			{
				if (S_CMPNOCASE(entry->getName(), map_lumps[a]))
				{
					known = true;
					a = NUMMAPLUMPS;
				}
			}
			if (!known)
				map.unk.push_back(entry);

			// Next entry
			entry = entry->nextEntry();
		}

		// If we got to the end before we found ENDMAP, something is wrong
		if (!entry)
			return mapdesc_t();

		// Set end entry
		map.end = entry;

		return map;
	}

	// Check for doom/hexen format map
	uint8_t existing_map_lumps[NUMMAPLUMPS];
	memset(existing_map_lumps, 0, NUMMAPLUMPS);
	ArchiveEntry* entry = maphead->nextEntry();
	while (entry)
	{
		// Check that the entry is a valid map-related entry
		bool mapentry = false;
		for (unsigned a = 0; a < NUMMAPLUMPS; a++)
		{
			if (S_CMPNOCASE(entry->getName(), map_lumps[a]))
			{
				mapentry = true;
				existing_map_lumps[a] = 1;
				break;
			}
			else if (a == LUMP_GL_HEADER)
			{
				string name = maphead->getName(true);
				name.Prepend("GL_");
				if (S_CMPNOCASE(entry->getName(), name))
				{
					mapentry = true;
					existing_map_lumps[a] = 1;
					break;
				}
			}
		}

		// If it wasn't a map entry, exit this loop
		if (!mapentry)
		{
			entry = entry->prevEntry();
			break;
		}

		// If we've reached the end of the archive, exit this loop
		if (!entry->nextEntry())
			break;

		// Go to next entry
		entry = entry->nextEntry();
	}

	// Check for the required map entries
	for (unsigned a = 0; a < 5; a++)
	{
		if (existing_map_lumps[a] == 0)
			return mapdesc_t();
	}

	// Setup map info
	map.head = maphead;
	map.end = entry;
	map.name = maphead->getName();

	// If BEHAVIOR lump exists, it's a hexen format map
	if (existing_map_lumps[LUMP_BEHAVIOR])
		map.format = MAP_HEXEN;
	// If LEAFS, LIGHTS and MACROS exist, it's a doom 64 format map
	else if (existing_map_lumps[LUMP_LEAFS] && existing_map_lumps[LUMP_LIGHTS]
	         && existing_map_lumps[LUMP_MACROS])
		map.format = MAP_DOOM64;
	// Otherwise it's doom format
	else
		map.format = MAP_DOOM;

	return map;
}
Beispiel #16
0
/* LfdArchive::write
 * Writes the lfd archive to a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool LfdArchive::write(MemChunk& mc, bool update)
{
	// Determine total size
	uint32_t dir_size = (numEntries() + 1)<<4;
	uint32_t total_size = dir_size;
	ArchiveEntry* entry = NULL;
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		total_size += 16;
		setEntryOffset(entry, total_size);
		if (update)
		{
			entry->setState(0);
			entry->exProp("Offset") = (int)total_size;
		}
		total_size += entry->getSize();
	}

	// Clear/init MemChunk
	mc.clear();
	mc.seek(0, SEEK_SET);
	mc.reSize(total_size);

	// Variables
	char type[5] = "RMAP";
	char name[9] = "resource";
	size_t size = wxINT32_SWAP_ON_BE(numEntries()<<4);


	// Write the resource map first
	mc.write(type, 4);
	mc.write(name, 8);
	mc.write(&size,4);
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		for (int t = 0; t < 5; ++t) type[t] = 0;
		for (int n = 0; n < 9; ++n) name[n] = 0;
		size = wxINT32_SWAP_ON_BE(entry->getSize());
		wxFileName fn(entry->getName());

		for (size_t c = 0; c < fn.GetName().length() && c < 9; c++)
			name[c] = fn.GetName()[c];
		for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++)
			type[c] = fn.GetExt()[c];

		mc.write(type, 4);
		mc.write(name, 8);
		mc.write(&size,4);
	}

	// Write the lumps
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		for (int t = 0; t < 5; ++t) type[t] = 0;
		for (int n = 0; n < 9; ++n) name[n] = 0;
		size = wxINT32_SWAP_ON_BE(entry->getSize());
		wxFileName fn(entry->getName());

		for (size_t c = 0; c < fn.GetName().length() && c < 9; c++)
			name[c] = fn.GetName()[c];
		for (size_t c = 0; c < fn.GetExt().length() && c < 5; c++)
			type[c] = fn.GetExt()[c];

		mc.write(type, 4);
		mc.write(name, 8);
		mc.write(&size,4);
		mc.write(entry->getData(), entry->getSize());
	}

	return true;
}
Beispiel #17
0
/* ADatArchive::open
 * Reads dat format data from a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool ADatArchive::open(MemChunk& mc) {
	// Check given data is valid
	if (mc.getSize() < 16)
		return false;

	// Read dat header
	char magic[4];
	long dir_offset;
	long dir_size;
	mc.seek(0, SEEK_SET);
	mc.read(magic, 4);
	mc.read(&dir_offset, 4);
	mc.read(&dir_size, 4);

	// Check it
	if (magic[0] != 'A' || magic[1] != 'D' || magic[2] != 'A' || magic[3] != 'T') {
		wxLogMessage("ADatArchive::open: Opening failed, invalid header");
		Global::error = "Invalid dat header";
		return false;
	}

	// Stop announcements (don't want to be announcing modification due to entries being added etc)
	setMuted(true);

	// Read the directory
	size_t num_entries = dir_size / DIRENTRY;
	mc.seek(dir_offset, SEEK_SET);
	theSplashWindow->setProgressMessage("Reading dat archive data");
	for (uint32_t d = 0; d < num_entries; d++) {
		// Update splash window progress
		theSplashWindow->setProgress(((float)d / (float)num_entries));

		// Read entry info
		char name[128];
		long offset;
		long decsize;
		long compsize;
		long whatever;			// No idea what this could be
		mc.read(name, 128);
		mc.read(&offset, 4);
		mc.read(&decsize, 4);
		mc.read(&compsize, 4);
		mc.read(&whatever, 4);

		// Byteswap if needed
		offset = wxINT32_SWAP_ON_BE(offset);
		decsize = wxINT32_SWAP_ON_BE(decsize);
		compsize = wxINT32_SWAP_ON_BE(compsize);

		// Check offset+size
		if ((unsigned)(offset + compsize) > mc.getSize()) {
			wxLogMessage("ADatArchive::open: dat archive is invalid or corrupt (entry goes past end of file)");
			Global::error = "Archive is invalid and/or corrupt";
			setMuted(false);
			return false;
		}

		// Parse name
		wxFileName fn(wxString::FromAscii(name, 128));

		// Create directory if needed
		ArchiveTreeNode* dir = createDir(fn.GetPath(true, wxPATH_UNIX));

		// Create entry
		ArchiveEntry* entry = new ArchiveEntry(fn.GetFullName(), compsize);
		entry->exProp("Offset") = (int)offset;
		entry->exProp("FullSize") = (int)decsize;
		entry->setLoaded(false);
		entry->setState(0);

		// Add to directory
		dir->addEntry(entry);
	}

	// Detect all entry types
	MemChunk edata;
	vector<ArchiveEntry*> all_entries;
	getEntryTreeAsList(all_entries);
	theSplashWindow->setProgressMessage("Detecting entry types");
	for (size_t a = 0; a < all_entries.size(); a++) {
		// Update splash window progress
		theSplashWindow->setProgress((((float)a / (float)num_entries)));

		// Get entry
		ArchiveEntry* entry = all_entries[a];

		// Read entry data if it isn't zero-sized
		if (entry->getSize() > 0) {
			// Read the entry data
			mc.exportMemChunk(edata, (int)entry->exProp("Offset"), entry->getSize());
			MemChunk xdata;
			if (Compression::ZlibInflate(edata, xdata, (int)entry->exProp("FullSize")))
				entry->importMemChunk(xdata);
			else {
				wxLogMessage("Entry %s couldn't be inflated", CHR(entry->getName()));
				entry->importMemChunk(edata);
			}
		}

		// Detect entry type
		EntryType::detectEntryType(entry);

		// Unload entry data if needed
		if (!archive_load_data)
			entry->unloadData();

		// Set entry to unchanged
		entry->setState(0);
	}

	// Setup variables
	setMuted(false);
	setModified(false);
	announce("opened");

	theSplashWindow->setProgressMessage("");

	return true;
}
Beispiel #18
0
/* EntryOperations::compileACS
 * Attempts to compile [entry] as an ACS script. If the entry is
 * named SCRIPTS, the compiled data is imported to the BEHAVIOR
 * entry previous to it, otherwise it is imported to a same-name
 * compiled library entry in the acs namespace
 *******************************************************************/
bool EntryOperations::compileACS(ArchiveEntry* entry, bool hexen, ArchiveEntry* target, wxFrame* parent)
{
	// Check entry was given
	if (!entry)
		return false;

	// Check entry has a parent (this is useless otherwise)
	if (!target && !entry->getParent())
		return false;

	// Check entry is text
	if (!EntryDataFormat::getFormat("text")->isThisFormat(entry->getMCData()))
	{
		wxMessageBox("Error: Entry does not appear to be text", "Error", wxOK|wxCENTRE|wxICON_ERROR);
		return false;
	}

	// Check if the ACC path is set up
	string accpath = path_acc;
	if (accpath.IsEmpty() || !wxFileExists(accpath))
	{
		wxMessageBox("Error: ACC path not defined, please configure in SLADE preferences", "Error", wxOK|wxCENTRE|wxICON_ERROR);
		PreferencesDialog::openPreferences(parent, "ACS");
		return false;
	}

	// Setup some path strings
	string srcfile = appPath(entry->getName(true) + ".acs", DIR_TEMP);
	string ofile = appPath(entry->getName(true) + ".o", DIR_TEMP);
	wxArrayString include_paths = wxSplit(path_acc_libs, ';');

	// Setup command options
	string opt;
	if (hexen)
		opt += " -h";
	if (!include_paths.IsEmpty())
	{
		for (unsigned a = 0; a < include_paths.size(); a++)
			opt += S_FMT(" -i \"%s\"", include_paths[a]);
	}

	// Find/export any resource libraries
	Archive::search_options_t sopt;
	sopt.match_type = EntryType::getType("acs");
	sopt.search_subdirs = true;
	vector<ArchiveEntry*> entries = theArchiveManager->findAllResourceEntries(sopt);
	wxArrayString lib_paths;
	for (unsigned a = 0; a < entries.size(); a++)
	{
		// Ignore SCRIPTS
		if (S_CMPNOCASE(entries[a]->getName(true), "SCRIPTS"))
			continue;

		// Ignore entries from other archives
		if (entry->getParent() &&
			(entry->getParent()->getFilename(true) != entries[a]->getParent()->getFilename(true)))
			continue;

		string path = appPath(entries[a]->getName(true) + ".acs", DIR_TEMP);
		entries[a]->exportFile(path);
		lib_paths.Add(path);
		LOG_MESSAGE(2, "Exporting ACS library %s", entries[a]->getName());
	}

	// Export script to file
	entry->exportFile(srcfile);

	// Execute acc
	string command = path_acc + " " + opt + " \"" + srcfile + "\" \"" + ofile + "\"";
	wxArrayString output;
	wxArrayString errout;
	theApp->SetTopWindow(parent);
	wxExecute(command, output, errout, wxEXEC_SYNC);
	theApp->SetTopWindow(theMainWindow);

	// Log output
	theConsole->logMessage("ACS compiler output:");
	string output_log;
	if (!output.IsEmpty())
	{
		const char *title1 = "=== Log: ===\n";
		theConsole->logMessage(title1);
		output_log += title1;
		for (unsigned a = 0; a < output.size(); a++)
		{
			theConsole->logMessage(output[a]);
			output_log += output[a];
		}
	}

	if (!errout.IsEmpty())
	{
		const char *title2 = "\n=== Error log: ===\n";
		theConsole->logMessage(title2);
		output_log += title2;
		for (unsigned a = 0; a < errout.size(); a++)
		{
			theConsole->logMessage(errout[a]);
			output_log += errout[a];
		}
	}

	// Delete source file
	wxRemoveFile(srcfile);

	// Delete library files
	for (unsigned a = 0; a < lib_paths.size(); a++)
		wxRemoveFile(lib_paths[a]);

	// Check it compiled successfully
	if (wxFileExists(ofile))
	{
		// If no target entry was given, find one
		if (!target)
		{
			// Check if the script is a map script (BEHAVIOR)
			if (S_CMPNOCASE(entry->getName(), "SCRIPTS"))
			{
				// Get entry before SCRIPTS
				ArchiveEntry* prev = entry->prevEntry();

				// Create a new entry there if it isn't BEHAVIOR
				if (!prev || !(S_CMPNOCASE(prev->getName(), "BEHAVIOR")))
					prev = entry->getParent()->addNewEntry("BEHAVIOR", entry->getParent()->entryIndex(entry));

				// Import compiled script
				prev->importFile(ofile);
			}
			else
			{
				// Otherwise, treat it as a library

				// See if the compiled library already exists as an entry
				Archive::search_options_t opt;
				opt.match_namespace = "acs";
				opt.match_name = entry->getName(true);
				if (entry->getParent()->getDesc().names_extensions)
				{
					opt.match_name += ".o";
					opt.ignore_ext = false;
				}
				ArchiveEntry* lib = entry->getParent()->findLast(opt);

				// If it doesn't exist, create it
				if (!lib)
					lib = entry->getParent()->addEntry(new ArchiveEntry(entry->getName(true) + ".o"), "acs");

				// Import compiled script
				lib->importFile(ofile);
			}
		}
		else
			target->importFile(ofile);

		// Delete compiled script file
		wxRemoveFile(ofile);
	}
	else
	{
		string errors;
		if (wxFileExists(appPath("acs.err", DIR_TEMP)))
		{
			// Read acs.err to string
			wxFile file(appPath("acs.err", DIR_TEMP));
			char* buf = new char[file.Length()];
			file.Read(buf, file.Length());
			errors = wxString::From8BitData(buf, file.Length());
			delete[] buf;
		}
		else
			errors = output_log;

		ExtMessageDialog dlg(NULL, "Error Compiling");
		dlg.setMessage("The following errors were encountered while compiling, please fix them and recompile:");
		dlg.setExt(errors);
		dlg.ShowModal();

		return false;
	}

	return true;
}
Beispiel #19
0
/* StyleSet::loadResourceStyles
 * Loads all text styles from the slade resource archive (slade.pk3)
 *******************************************************************/
bool StyleSet::loadResourceStyles()
{
	// Get 'config/text_styles' directory in slade.pk3
	ArchiveTreeNode* dir = theArchiveManager->programResourceArchive()->getDir("config/text_styles");

	// Check it exists
	if (!dir)
	{
		wxLogMessage("Warning: No 'config/text_styles' directory exists in slade.pk3");
		return false;
	}

	// Read default style set first
	ArchiveEntry* default_style = dir->getEntry("default.sss");
	if (default_style)
	{
		// Read entry data into tokenizer
		Tokenizer tz;
		tz.openMem(&default_style->getMCData(), default_style->getName());

		// Parse it
		ParseTreeNode root;
		root.allowDup(true);
		root.parse(tz);

		// Read any styleset definitions
		vector<STreeNode*> nodes = root.getChildren("styleset");
		for (unsigned b = 0; b  < nodes.size(); b++)
		{
			StyleSet* newset = new StyleSet();
			if (newset->parseSet((ParseTreeNode*)nodes[b]))
				style_sets.push_back(newset);
			else
				delete newset;
		}
	}

	// Go through all entries within it
	for (unsigned a = 0; a < dir->numEntries(); a++)
	{
		ArchiveEntry* entry = dir->getEntry(a);

		// Skip default
		if (entry->getName(true) == "default")
			continue;

		// Read entry data into tokenizer
		Tokenizer tz;
		tz.openMem(&entry->getMCData(), entry->getName());

		// Parse it
		ParseTreeNode root;
		root.allowDup(true);
		root.parse(tz);

		// Read any styleset definitions
		vector<STreeNode*> nodes = root.getChildren("styleset");
		for (unsigned b = 0; b  < nodes.size(); b++)
		{
			StyleSet* newset = new StyleSet();
			if (newset->parseSet((ParseTreeNode*)nodes[b]))
				style_sets.push_back(newset);
			else
				delete newset;
		}
	}

	return true;
}
Beispiel #20
0
/* WadArchive::findLast
 * Returns the last entry matching the search criteria in [options],
 * or NULL if no matching entry was found
 *******************************************************************/
ArchiveEntry* WadArchive::findLast(search_options_t& options)
{
	// Init search variables
	ArchiveEntry* start = getEntry(numEntries()-1);
	ArchiveEntry* end = NULL;
	options.match_name = options.match_name.Lower();

	// "graphics" namespace is the global namespace in a wad
	if (options.match_namespace == "graphics")
		options.match_namespace = "";

	// "global" namespace has no name, by the way
	if (options.match_namespace == "global")
		options.match_namespace = "";

	// Check for namespace to search
	if (!options.match_namespace.IsEmpty())
	{
		// Find matching namespace
		bool ns_found = false;
		for (unsigned a = 0; a < namespaces.size(); a++)
		{
			if (namespaces[a].name == options.match_namespace)
			{
				start = namespaces[a].end->prevEntry();
				end = namespaces[a].start;
				ns_found = true;
				break;
			}
		}

		// Return none if namespace not found
		if (!ns_found)
			return NULL;
	}

	// Begin search
	ArchiveEntry* entry = start;
	while (entry != end)
	{
		// Check type
		if (options.match_type)
		{
			if (entry->getType() == EntryType::unknownType())
			{
				if (!options.match_type->isThisType(entry))
				{
					entry = entry->prevEntry();
					continue;
				}
			}
			else if (options.match_type != entry->getType())
			{
				entry = entry->prevEntry();
				continue;
			}
		}

		// Check name
		if (!options.match_name.IsEmpty())
		{
			if (!options.match_name.Matches(entry->getName().Lower()))
			{
				entry = entry->prevEntry();
				continue;
			}
		}

		// Entry passed all checks so far, so we found a match
		return entry;
	}

	// No match found
	return NULL;
}
Beispiel #21
0
	bool loadIconsDir(int type, ArchiveTreeNode* dir)
	{
		if (!dir)
			return false;

		// Check for icon set dirs
		for (unsigned a = 0; a < dir->nChildren(); a++)
		{
			if (dir->getChild(a)->getName() != "large")
			{
				if (type == GENERAL)
					iconsets_general.push_back(dir->getChild(a)->getName());
				else if (type == ENTRY)
					iconsets_entry.push_back(dir->getChild(a)->getName());
			}
		}

		// Get icon set dir
		string icon_set_dir = "Default";
		if (type == ENTRY) icon_set_dir = iconset_entry_list;
		if (type == GENERAL) icon_set_dir = iconset_general;
		if (icon_set_dir != "Default" && dir->getChild(icon_set_dir))
			dir = (ArchiveTreeNode*)dir->getChild(icon_set_dir);

		vector<icon_t>& icons = iconList(type);
		string tempfile = appPath("sladetemp", DIR_TEMP);

		// Go through each entry in the directory
		for (size_t a = 0; a < dir->numEntries(false); a++)
		{
			ArchiveEntry* entry = dir->getEntry(a);

			// Ignore anything not png format
			if (!entry->getName().EndsWith("png"))
				continue;

			// Export entry data to a temporary file
			entry->exportFile(tempfile);

			// Create / setup icon
			icon_t n_icon;
			n_icon.image.LoadFile(tempfile);	// Load image from temp file
			n_icon.name = entry->getName(true);	// Set icon name
			n_icon.resource_entry = entry;

			// Add the icon
			icons.push_back(n_icon);

			// Delete the temporary file
			wxRemoveFile(tempfile);
		}

		// Go through large icons
		ArchiveTreeNode* dir_large = (ArchiveTreeNode*)dir->getChild("large");
		if (dir_large)
		{
			for (size_t a = 0; a < dir_large->numEntries(false); a++)
			{
				ArchiveEntry* entry = dir_large->getEntry(a);

				// Ignore anything not png format
				if (!entry->getName().EndsWith("png"))
					continue;

				// Export entry data to a temporary file
				entry->exportFile(tempfile);

				// Create / setup icon
				bool found = false;
				string name = entry->getName(true);
				for (unsigned i = 0; i < icons.size(); i++)
				{
					if (icons[i].name == name)
					{
						icons[i].image_large.LoadFile(tempfile);
						found = true;
						break;
					}
				}

				if (!found)
				{
					icon_t n_icon;
					n_icon.image_large.LoadFile(tempfile);	// Load image from temp file
					n_icon.name = entry->getName(true);		// Set icon name
					n_icon.resource_entry = entry;

					// Add the icon
					icons.push_back(n_icon);
				}

				// Delete the temporary file
				wxRemoveFile(tempfile);
			}
		}

		return true;
	}
Beispiel #22
0
/* WadArchive::updateNamespaces
 * Updates the namespace list
 *******************************************************************/
void WadArchive::updateNamespaces()
{
	// Clear current namespace info
	while (namespaces.size() > 0)
		namespaces.pop_back();

	// Go through all entries
	for (unsigned a = 0; a < numEntries(); a++)
	{
		ArchiveEntry* entry = getRoot()->getEntry(a);

		// Check for namespace begin
		if (entry->getName().Matches("*_START"))
		{
			// Create new namespace
			wad_ns_pair_t ns(entry, NULL);
			string name = entry->getName();
			ns.name = name.Left(name.Length() - 6).Lower();
			ns.start_index = entryIndex(ns.start);

			// Convert some special cases (because technically PP_START->P_END is a valid namespace)
			if (ns.name == "pp")
				ns.name = "p";
			if (ns.name == "ff")
				ns.name = "f";
			if (ns.name == "ss")
				ns.name = "s";
			if (ns.name == "tt")
				ns.name = "t";

			// Add to namespace list
			namespaces.push_back(ns);
		}
		// Check for namespace end
		else if (entry->getName().Matches("?_END") || entry->getName().Matches("??_END"))
		{
			// Get namespace 'name'
			int len = entry->getName().Length() - 4;
			string ns_name = entry->getName().Left(len).Lower();

			// Convert some special cases (because technically P_START->PP_END is a valid namespace)
			if (ns_name == "pp")
				ns_name = "p";
			if (ns_name == "ff")
				ns_name = "f";
			if (ns_name == "ss")
				ns_name = "s";
			if (ns_name == "tt")
				ns_name = "t";

			// Check if it's the end of an existing namespace
			// Remember entry is getEntry(a)? index is 'a'
			//size_t index = entryIndex(entry);

			bool found = false;
			for (unsigned b = 0; b < namespaces.size(); b++)
			{
				// Can't close a namespace that starts afterwards
				if (namespaces[b].start_index > a)
					break;
				// Can't close an already-closed namespace
				if (namespaces[b].end != NULL)
					continue;
				if (S_CMP(ns_name, namespaces[b].name))
				{
					found = true;
					namespaces[b].end = entry;
					namespaces[b].end_index = a;
					break;
				}
			}
			// Flat hack: closing the flat namespace without opening it
			if (found == false && ns_name == "f")
			{
				wad_ns_pair_t ns(getRoot()->getEntry(0), entry);
				ns.start_index = 0;
				ns.end_index = a;
				ns.name = "f";
				namespaces.push_back(ns);
			}
		}
	}

	// ROTT stuff. The first lump in the archive is always WALLSTRT, the last lump is either
	// LICENSE (darkwar.wad) or VENDOR (huntbgin.wad), with TABLES just before in both cases.
	// The shareware version has 2091 lumps, the complete version has about 50% more.
	if (numEntries() > 2090 && getRoot()->getEntry(0)->getName().Matches("WALLSTRT") &&
	        getRoot()->getEntry(numEntries()-2)->getName().Matches("TABLES"))
	{
		wad_ns_pair_t ns(getRoot()->getEntry(0), getRoot()->getEntry(numEntries()-1));
		ns.name = "rott";
		ns.start_index = 0;
		ns.end_index = entryIndex(ns.end);
		namespaces.push_back(ns);
	}


	// Check namespaces
	for (unsigned a = 0; a < namespaces.size(); a++)
	{
		wad_ns_pair_t& ns = namespaces[a];

		// Check the namespace has an end
		if (!ns.end)
		{
			// If not, remove the namespace as it is invalid
			namespaces.erase(namespaces.begin() + a);
			a--;
			continue;
		}

		// Check namespace name for special cases
		for (int n = 0; n < n_special_namespaces; n++)
		{
			if (S_CMP(ns.name, special_namespaces[n].letter))
				ns.name = special_namespaces[n].name;
		}

		ns.start_index = entryIndex(ns.start);
		ns.end_index = entryIndex(ns.end);

		// Testing
		//wxLogMessage("Namespace %s from %s (%d) to %s (%d)", ns.name,
		//	ns.start->getName(), ns.start_index, ns.end->getName(), ns.end_index);
	}
}
Beispiel #23
0
/* WadArchive::findAll
 * Returns all entries matching the search criteria in [options]
 *******************************************************************/
vector<ArchiveEntry*> WadArchive::findAll(search_options_t& options)
{
	// Init search variables
	ArchiveEntry* start = getEntry(0);
	ArchiveEntry* end = NULL;
	options.match_name = options.match_name.Lower();
	vector<ArchiveEntry*> ret;

	// "graphics" namespace is the global namespace in a wad
	if (options.match_namespace == "graphics")
		options.match_namespace = "";

	// Check for namespace to search
	if (!options.match_namespace.IsEmpty())
	{
		// Find matching namespace
		bool ns_found = false;
		for (unsigned a = 0; a < namespaces.size(); a++)
		{
			if (namespaces[a].name == options.match_namespace)
			{
				start = namespaces[a].start->nextEntry();
				end = namespaces[a].end;
				ns_found = true;
				break;
			}
		}

		// Return none if namespace not found
		if (!ns_found)
			return ret;
	}

	ArchiveEntry* entry = start;
	while (entry != end)
	{
		// Check type
		if (options.match_type)
		{
			if (entry->getType() == EntryType::unknownType())
			{
				if (!options.match_type->isThisType(entry))
				{
					entry = entry->nextEntry();
					continue;
				}
			}
			else if (options.match_type != entry->getType())
			{
				entry = entry->nextEntry();
				continue;
			}
		}

		// Check name
		if (!options.match_name.IsEmpty())
		{
			// Force case insensitivity
			options.match_name.MakeLower();

			if (!options.match_name.Matches(entry->getName().Lower()))
			{
				entry = entry->nextEntry();
				continue;
			}
		}

		// Entry passed all checks so far, so we found a match
		ret.push_back(entry);
		entry = entry->nextEntry();
	}

	// Return search result
	return ret;
}
Beispiel #24
0
// ----------------------------------------------------------------------------
// ArchiveEntryList::applyFilter
//
// Applies the current filter(s) to the list
// ----------------------------------------------------------------------------
void ArchiveEntryList::applyFilter()
{
	// Clear current filter list
	items.clear();

	// Check if any filters were given
	if (filter_text.IsEmpty() && filter_category.IsEmpty())
	{
		// No filter, just refresh the list
		unsigned count = current_dir->numEntries() + current_dir->nChildren();
		for (unsigned a = 0; a < count; a++)
			items.push_back(a);
		updateList();

		return;
	}

	// Filter by category
	unsigned index = 0;
	ArchiveEntry* entry = getEntry(index, false);
	while (entry)
	{
		if (filter_category.IsEmpty() || entry->getType() == EntryType::folderType())
			items.push_back(index);	// If no category specified, just add all entries to the filter
		else
		{
			// Check for category match
			if (S_CMPNOCASE(entry->getType()->getCategory(), filter_category))
				items.push_back(index);
		}

		entry = getEntry(++index, false);
	}

	// Now filter by name if needed
	if (!filter_text.IsEmpty())
	{
		// Split filter by ,
		wxArrayString terms = wxSplit(filter_text, ',');

		// Process filter strings
		for (unsigned a = 0; a < terms.size(); a++)
		{
			// Remove spaces
			terms[a].Replace(" ", "");

			// Set to lowercase and add * to the end
			if (!terms[a].IsEmpty()) terms[a] = terms[a].Lower() + "*";
		}

		// Go through filtered list
		for (unsigned a = 0; a < items.size(); a++)
		{
			entry = getEntry(items[a], false);

			// Don't filter folders if !elist_filter_dirs
			if (!elist_filter_dirs && entry->getType() == EntryType::folderType())
				continue;

			// Check for name match with filter
			bool match = false;
			for (unsigned b = 0; b < terms.size(); b++)
			{
				if (entry == entry_dir_back || entry->getName().Lower().Matches(terms[b]))
				{
					match = true;
					continue;
				}
			}
			if (match)
				continue;

			// No match, remove from filtered list
			items.erase(items.begin() + a);
			a--;
		}
	}

	// Update the list
	updateList();
}
Beispiel #25
0
/* WadArchive::open
 * Reads wad format data from a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool WadArchive::open(MemChunk& mc)
{
	// Check data was given
	if (!mc.hasData())
		return false;

	// Read wad header
	uint32_t	num_lumps = 0;
	uint32_t	dir_offset = 0;
	char		wad_type[4] = "";
	mc.seek(0, SEEK_SET);
	mc.read(&wad_type, 4);		// Wad type
	mc.read(&num_lumps, 4);		// No. of lumps in wad
	mc.read(&dir_offset, 4);	// Offset to directory

	// Byteswap values for big endian if needed
	num_lumps = wxINT32_SWAP_ON_BE(num_lumps);
	dir_offset = wxINT32_SWAP_ON_BE(dir_offset);

	// Check the header
	if (wad_type[1] != 'W' || wad_type[2] != 'A' || wad_type[3] != 'D')
	{
		wxLogMessage("WadArchive::openFile: File %s has invalid header", filename);
		Global::error = "Invalid wad header";
		return false;
	}

	// Check for iwad
	if (wad_type[0] == 'I')
		iwad = true;

	// Stop announcements (don't want to be announcing modification due to entries being added etc)
	setMuted(true);

	vector<uint32_t> offsets;

	// Read the directory
	mc.seek(dir_offset, SEEK_SET);
	theSplashWindow->setProgressMessage("Reading wad archive data");
	for (uint32_t d = 0; d < num_lumps; d++)
	{
		// Update splash window progress
		theSplashWindow->setProgress(((float)d / (float)num_lumps));

		// Read lump info
		char name[9] = "";
		uint32_t offset = 0;
		uint32_t size = 0;

		mc.read(&offset, 4);	// Offset
		mc.read(&size, 4);		// Size
		mc.read(name, 8);		// Name
		name[8] = '\0';

		// Byteswap values for big endian if needed
		offset = wxINT32_SWAP_ON_BE(offset);
		size = wxINT32_SWAP_ON_BE(size);

		// Check to catch stupid shit
		if (size > 0)
		{
			if (offset == 0)
			{
				LOG_MESSAGE(2, "No.");
				continue;
			}
			if (VECTOR_EXISTS(offsets, offset))
			{
				LOG_MESSAGE(1, "Ignoring entry %d: %s, is a clone of a previous entry", d, name);
				continue;
			}
			offsets.push_back(offset);
		}

		// Hack to open Operation: Rheingold WAD files
		if (size == 0 && offset > mc.getSize())
			offset = 0;

		// Is there a compression/encryption thing going on?
		bool jaguarencrypt = !!(name[0] & 0x80);	// look at high bit
		name[0] = name[0] & 0x7F;					// then strip it away

		// Look for encryption shenanigans
		size_t actualsize = size;
		if (jaguarencrypt)
		{
			if (d < num_lumps - 1)
			{
				size_t pos = mc.currentPos();
				uint32_t nextoffset = 0;
				for (int i = 0; i + d < num_lumps; ++i)
				{
					mc.read(&nextoffset, 4);
					if (nextoffset != 0) break;
					mc.seek(12, SEEK_CUR);
				}
				nextoffset = wxINT32_SWAP_ON_BE(nextoffset);
				if (nextoffset == 0) nextoffset = dir_offset;
				mc.seek(pos, SEEK_SET);
				actualsize = nextoffset - offset;
			}
			else
			{
				if (offset > dir_offset)
				{
					actualsize = mc.getSize() - offset;
				}
				else
				{
					actualsize = dir_offset - offset;
				}
			}
		}

		// If the lump data goes past the end of the file,
		// the wadfile is invalid
		if (offset + actualsize > mc.getSize())
		{
			wxLogMessage("WadArchive::open: Wad archive is invalid or corrupt");
			Global::error = S_FMT("Archive is invalid and/or corrupt (lump %d: %s data goes past end of file)", d, name);
			setMuted(false);
			return false;
		}

		// Create & setup lump
		ArchiveEntry* nlump = new ArchiveEntry(wxString::FromAscii(name), size);
		nlump->setLoaded(false);
		nlump->exProp("Offset") = (int)offset;
		nlump->setState(0);

		if (jaguarencrypt)
		{
			nlump->setEncryption(ENC_JAGUAR);
			nlump->exProp("FullSize") = (int)size;
		}

		// Add to entry list
		getRoot()->addEntry(nlump);
	}

	// Detect namespaces (needs to be done before type detection as some types
	// rely on being within certain namespaces)
	updateNamespaces();

	// Detect all entry types
	MemChunk edata;
	theSplashWindow->setProgressMessage("Detecting entry types");
	for (size_t a = 0; a < numEntries(); a++)
	{
		// Update splash window progress
		theSplashWindow->setProgress((((float)a / (float)numEntries())));

		// Get entry
		ArchiveEntry* entry = getEntry(a);

		// Read entry data if it isn't zero-sized
		if (entry->getSize() > 0)
		{
			// Read the entry data
			mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize());
			if (entry->isEncrypted())
			{
				if (entry->exProps().propertyExists("FullSize")
				        && (unsigned)(int)(entry->exProp("FullSize")) >  entry->getSize())
					edata.reSize((int)(entry->exProp("FullSize")), true);
				if (!JaguarDecode(edata))
					wxLogMessage("%i: %s (following %s), did not decode properly", a, entry->getName(), a>0?getEntry(a-1)->getName():"nothing");
			}
			entry->importMemChunk(edata);
		}

		// Detect entry type
		EntryType::detectEntryType(entry);

		// Unload entry data if needed
		if (!archive_load_data)
			entry->unloadData();

		// Set entry to unchanged
		entry->setState(0);
	}

	// Identify #included lumps (DECORATE, GLDEFS, etc.)
	detectIncludes();

	// Detect maps (will detect map entry types)
	theSplashWindow->setProgressMessage("Detecting maps");
	detectMaps();

	// Setup variables
	setMuted(false);
	setModified(false);
	//if (iwad && iwad_lock) read_only = true;
	announce("opened");

	theSplashWindow->setProgressMessage("");

	return true;
}
Beispiel #26
0
/* WadArchive::write
 * Writes the wad archive to a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool WadArchive::write(MemChunk& mc, bool update)
{
	// Don't write if iwad
	if (iwad && iwad_lock)
	{
		Global::error = "IWAD saving disabled";
		return false;
	}

	// Determine directory offset & individual lump offsets
	uint32_t dir_offset = 12;
	ArchiveEntry* entry = NULL;
	for (uint32_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		setEntryOffset(entry, dir_offset);
		dir_offset += entry->getSize();
	}

	// Clear/init MemChunk
	mc.clear();
	mc.seek(0, SEEK_SET);
	mc.reSize(dir_offset + numEntries() * 16);

	// Setup wad type
	char wad_type[4] = { 'P', 'W', 'A', 'D' };
	if (iwad) wad_type[0] = 'I';

	// Write the header
	uint32_t num_lumps = numEntries();
	mc.write(wad_type, 4);
	mc.write(&num_lumps, 4);
	mc.write(&dir_offset, 4);

	// Write the lumps
	for (uint32_t l = 0; l < num_lumps; l++)
	{
		entry = getEntry(l);
		mc.write(entry->getData(), entry->getSize());
	}

	// Write the directory
	for (uint32_t l = 0; l < num_lumps; l++)
	{
		entry = getEntry(l);
		char name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
		long offset = getEntryOffset(entry);
		long size = entry->getSize();

		for (size_t c = 0; c < entry->getName().length() && c < 8; c++)
			name[c] = entry->getName()[c];

		mc.write(&offset, 4);
		mc.write(&size, 4);
		mc.write(name, 8);

		if (update)
		{
			entry->setState(0);
			entry->exProp("Offset") = (int)offset;
		}
	}

	return true;
}
Beispiel #27
0
/* Archive::findAll
 * Returns a list of entries matching the search criteria in
 * [options]
 *******************************************************************/
vector<ArchiveEntry*> Archive::findAll(search_options_t& options)
{
	// Init search variables
	ArchiveTreeNode* dir = options.dir;
	if (!dir) dir = dir_root;
	vector<ArchiveEntry*> ret;
	options.match_name.MakeLower();		// Force case-insensitive

	// Begin search

	// Search entries
	for (unsigned a = 0; a < dir->numEntries(); a++)
	{
		ArchiveEntry* entry = dir->getEntry(a);

		// Check type
		if (options.match_type)
		{
			if (entry->getType() == EntryType::unknownType())
			{
				if (!options.match_type->isThisType(entry))
					continue;
			}
			else if (options.match_type != entry->getType())
				continue;
		}

		// Check name
		if (!options.match_name.IsEmpty())
		{
			// Cut extension if ignoring
			wxFileName fn(entry->getName());
			if (options.ignore_ext)
			{
				if (!fn.GetName().MakeLower().Matches(options.match_name))
					continue;
			}
			else if (!fn.GetFullName().MakeLower().Matches(options.match_name))
				continue;
		}

		// Check namespace
		if (!options.match_namespace.IsEmpty())
		{
			if (!(S_CMPNOCASE(detectNamespace(entry), options.match_namespace)))
				continue;
		}

		// Entry passed all checks so far, so we found a match
		ret.push_back(entry);
	}

	// Search subdirectories (if needed)
	if (options.search_subdirs)
	{
		for (unsigned a = 0; a < dir->nChildren(); a++)
		{
			search_options_t opt = options;
			opt.dir = (ArchiveTreeNode*)dir->getChild(a);

			// Add any matches to the list
			vector<ArchiveEntry*> vec = findAll(opt);
			ret.insert(ret.end(), vec.begin(), vec.end());
		}
	}

	// Return matches
	return ret;
}
Beispiel #28
0
/* DatArchive::write
 * Writes the dat archive to a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool DatArchive::write(MemChunk& mc, bool update)
{
	// Only two bytes are used for storing entry amount,
	// so abort for excessively large files:
	if (numEntries() > 65535)
		return false;

	// Determine directory offset, name offsets & individual lump offsets
	uint32_t dir_offset = 10;
	uint16_t name_offset = numEntries() * 12;
	uint32_t name_size = 0;
	string previousname = "";
	uint16_t* nameoffsets = new uint16_t[numEntries()];
	ArchiveEntry* entry = NULL;
	for (uint16_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		setEntryOffset(entry, dir_offset);
		dir_offset += entry->getSize();

		// Does the entry has a name?
		string name = entry->getName();
		if (l > 0 && previousname.length() > 0 && name.length() > previousname.length() &&
		        !previousname.compare(0, previousname.length(), name, 0, previousname.length()) &&
		        name.at(previousname.length()) == '+')
		{
			// This is a fake name
			name = "";
			nameoffsets[l] = 0;
		}
		else
		{
			// This is a true name
			previousname = name;
			nameoffsets[l] = uint16_t(name_offset + name_size);
			name_size += name.length() + 1;
		}
	}

	// Clear/init MemChunk
	mc.clear();
	mc.seek(0, SEEK_SET);
	mc.reSize(dir_offset + name_size + numEntries() * 12);

	// Write the header
	uint16_t num_lumps = wxINT16_SWAP_ON_BE(numEntries());
	dir_offset = wxINT32_SWAP_ON_BE(dir_offset);
	uint32_t unknown = 0;
	mc.write(&num_lumps, 2);
	mc.write(&dir_offset, 4);
	mc.write(&unknown, 4);

	// Write the lumps
	for (uint16_t l = 0; l < numEntries(); l++)
	{
		entry = getEntry(l);
		mc.write(entry->getData(), entry->getSize());
	}

	// Write the directory
	for (uint16_t l = 0; l < num_lumps; l++)
	{
		entry = getEntry(l);

		uint32_t offset = wxINT32_SWAP_ON_BE(getEntryOffset(entry));
		uint32_t size = wxINT32_SWAP_ON_BE(entry->getSize());
		uint16_t nameofs = wxINT16_SWAP_ON_BE(nameoffsets[l]);
		uint16_t flags = wxINT16_SWAP_ON_BE((entry->isEncrypted() == ENC_SCRLE0) ? 1 : 0);

		mc.write(&offset,	4);		// Offset
		mc.write(&size,		4);		// Size
		mc.write(&nameofs,	2);		// Name offset
		mc.write(&flags,	2);		// Flags

		if (update)
		{
			entry->setState(0);
			entry->exProp("Offset") = (int)wxINT32_SWAP_ON_BE(offset);
		}
	}

	// Write the names
	for (uint16_t l = 0; l < num_lumps; l++)
	{
		uint8_t zero = 0;
		entry = getEntry(l);
		if (nameoffsets[l])
		{
			mc.write(CHR(entry->getName()), entry->getName().length());
			mc.write(&zero, 1);
		}
	}

	// Clean-up
	delete[] nameoffsets;

	// Finished!
	return true;
}
Beispiel #29
0
/* DatArchive::open
 * Reads wad format data from a MemChunk
 * Returns true if successful, false otherwise
 *******************************************************************/
bool DatArchive::open(MemChunk& mc)
{
	// Check data was given
	if (!mc.hasData())
		return false;

	const uint8_t* mcdata = mc.getData();

	// Read dat header
	mc.seek(0, SEEK_SET);
	uint16_t num_lumps;
	uint32_t dir_offset, unknown;
	mc.read(&num_lumps, 2);		// Size
	mc.read(&dir_offset, 4);	// Directory offset
	mc.read(&unknown, 4);		// Unknown value
	num_lumps	= wxINT16_SWAP_ON_BE(num_lumps);
	dir_offset	= wxINT32_SWAP_ON_BE(dir_offset);
	unknown		= wxINT32_SWAP_ON_BE(unknown);
	string lastname(wxString::FromAscii("-noname-"));
	size_t namecount = 0;

	// Stop announcements (don't want to be announcing modification due to entries being added etc)
	setMuted(true);

	// Read the directory
	mc.seek(dir_offset, SEEK_SET);
	theSplashWindow->setProgressMessage("Reading dat archive data");
	for (uint32_t d = 0; d < num_lumps; d++)
	{
		// Update splash window progress
		theSplashWindow->setProgress(((float)d / (float)num_lumps));

		// Read lump info
		uint32_t offset = 0;
		uint32_t size = 0;
		uint16_t nameofs = 0;
		uint16_t flags = 0;

		mc.read(&offset,	4);		// Offset
		mc.read(&size,		4);		// Size
		mc.read(&nameofs,	2);		// Name offset
		mc.read(&flags,		2);		// Flags (only one: RLE encoded)

		// Byteswap values for big endian if needed
		offset = wxINT32_SWAP_ON_BE(offset);
		size = wxINT32_SWAP_ON_BE(size);
		nameofs = wxINT16_SWAP_ON_BE(nameofs);
		flags = wxINT16_SWAP_ON_BE(flags);

		// If the lump data goes past the directory,
		// the data file is invalid
		if (offset + size > mc.getSize())
		{
			wxLogMessage("DatArchive::open: Dat archive is invalid or corrupt at entry %i", d);
			Global::error = "Archive is invalid and/or corrupt";
			setMuted(false);
			return false;
		}

		string myname;
		if (nameofs != 0)
		{
			size_t len = 1;
			size_t start = nameofs+dir_offset;
			for (size_t i = start; mcdata[i] != 0; ++i) { ++len; }
			lastname = myname = wxString::FromAscii(mcdata+start, len);
			namecount = 0;
		}
		else
		{
			myname = S_FMT("%s+%d", lastname, ++namecount);
		}

		// Create & setup lump
		ArchiveEntry* nlump = new ArchiveEntry(myname, size);
		nlump->setLoaded(false);
		nlump->exProp("Offset") = (int)offset;
		nlump->setState(0);

		if (flags & 1) nlump->setEncryption(ENC_SCRLE0);

		// Check for markers
		if (!nlump->getName().Cmp("startflats"))
			flats[0] = d;
		if (!nlump->getName().Cmp("endflats"))
			flats[1] = d;
		if (!nlump->getName().Cmp("startsprites"))
			sprites[0] = d;
		if (!nlump->getName().Cmp("endmonsters"))
			sprites[1] = d;
		if (!nlump->getName().Cmp("startwalls"))
			walls[0] = d;
		if (!nlump->getName().Cmp("endwalls"))
			walls[1] = d;

		// Add to entry list
		getRoot()->addEntry(nlump);
	}

	// Detect all entry types
	MemChunk edata;
	theSplashWindow->setProgressMessage("Detecting entry types");
	for (size_t a = 0; a < numEntries(); a++)
	{
		// Update splash window progress
		theSplashWindow->setProgress((((float)a / (float)num_lumps)));

		// Get entry
		ArchiveEntry* entry = getEntry(a);

		// Read entry data if it isn't zero-sized
		if (entry->getSize() > 0)
		{
			// Read the entry data
			mc.exportMemChunk(edata, getEntryOffset(entry), entry->getSize());
			entry->importMemChunk(edata);
		}

		// Detect entry type
		EntryType::detectEntryType(entry);

		// Set entry to unchanged
		entry->setState(0);
	}

	// Detect maps (will detect map entry types)
	//theSplashWindow->setProgressMessage("Detecting maps");
	//detectMaps();

	// Setup variables
	setMuted(false);
	setModified(false);
	announce("opened");

	theSplashWindow->setProgressMessage("");

	return true;
}
Beispiel #30
0
/* WadArchive::detectMaps
 * Searches for any maps in the wad and adds them to the map list
 *******************************************************************/
vector<Archive::mapdesc_t> WadArchive::detectMaps()
{
	vector<mapdesc_t> maps;

	// Go through all lumps
	ArchiveEntry* entry = getEntry(0);
	bool lastentryismapentry = false;
	while (entry)
	{
		// UDMF format map check ********************************************************

		// Check for UDMF format map lump (TEXTMAP lump)
		if (entry->getName() == "TEXTMAP" && entry->prevEntry())
		{
			// Get map info
			mapdesc_t md = getMapInfo(entry->prevEntry());

			// Add to map list
			if (md.head != NULL)
			{
				entry = md.end;
				maps.push_back(md);
			}

			// Current index is ENDMAP, we don't want to check for a doom/hexen format
			// map so just go to the next index and continue the loop
			entry = entry->nextEntry();
			continue;
		}

		// Doom/Hexen format map check **************************************************
		// TODO maybe get rid of code duplication by calling getMapInfo() here too?

		// Array to keep track of what doom/hexen map lumps have been found
		uint8_t existing_map_lumps[NUMMAPLUMPS];
		memset(existing_map_lumps, 0, NUMMAPLUMPS);

		// Check if the current lump is a doom/hexen map lump
		bool maplump_found = false;
		for (int a = 0; a < 5; a++)
		{
			// Compare with all base map lump names
			if (S_CMP(entry->getName(), map_lumps[a]))
			{
				maplump_found = true;
				existing_map_lumps[a] = 1;
				break;
			}
		}

		// If we've found what might be a map
		if (maplump_found && entry->prevEntry())
		{
			// Save map header entry
			ArchiveEntry* header_entry = entry->prevEntry();

			// Check off map lumps until we find a non-map lump
			bool done = false;
			while (!done)
			{
				// Loop will end if no map lump is found
				done = true;

				// Compare with all map lump names
				for (int a = 0; a < NUMMAPLUMPS; a++)
				{
					// Compare with all base map lump names
					if (S_CMP(entry->getName(), map_lumps[a]))
					{
						existing_map_lumps[a] = 1;
						done = false;
						break;
					}
				}

				// If we're at the end of the wad, exit the loop
				if (!entry->nextEntry())
				{
					lastentryismapentry = true;
					break;
				}

				// Go to next lump if there is one
				if (!lastentryismapentry) entry = entry->nextEntry();
			}

			// Go back to the lump just after the last map lump found, but only if we actually moved
			if (!lastentryismapentry) entry = entry->prevEntry();

			// Check that we have all the required map lumps: VERTEXES, LINEDEFS, SIDEDEFS, THINGS & SECTORS
			if (!memchr(existing_map_lumps, 0, 5))
			{
				// Get map info
				mapdesc_t md;
				md.head = header_entry;				// Header lump
				md.name = header_entry->getName();	// Map title
				md.end = lastentryismapentry ?		// End lump
				         entry : entry->prevEntry();

				// If BEHAVIOR lump exists, it's a hexen format map
				if (existing_map_lumps[LUMP_BEHAVIOR])
					md.format = MAP_HEXEN;
				// If LEAFS, LIGHTS and MACROS exist, it's a doom 64 format map
				else if (existing_map_lumps[LUMP_LEAFS] && existing_map_lumps[LUMP_LIGHTS]
				         && existing_map_lumps[LUMP_MACROS])
					md.format = MAP_DOOM64;
				// Otherwise it's doom format
				else
					md.format = MAP_DOOM;

				// Add map info to the maps list
				maps.push_back(md);
			}
		}

		// Embedded WAD check (for Doom 64)
		if (entry->getType()->getFormat() == "archive_wad")
		{
			// Detect map format (probably kinda slow but whatever, no better way to do it really)
			Archive* tempwad = new WadArchive();
			tempwad->open(entry);
			vector<mapdesc_t> emaps = tempwad->detectMaps();
			if (emaps.size() > 0)
			{
				mapdesc_t md;
				md.head = entry;
				md.end = entry;
				md.archive = true;
				md.name = entry->getName(true).Upper();
				md.format = emaps[0].format;
				maps.push_back(md);
			}
			delete tempwad;
			entry->unlock();
		}

		// Not a UDMF or Doom/Hexen map lump, go to next lump
		entry = entry->nextEntry();
	}

	// Set all map header entries to ETYPE_MAP type
	for (size_t a = 0; a < maps.size(); a++)
		if (!maps[a].archive)
			maps[a].head->setType(EntryType::mapMarkerType());

	// Update entry map format hints
	for (unsigned a = 0; a < maps.size(); a++)
	{
		string format;
		if (maps[a].format == MAP_DOOM)
			format = "doom";
		else if (maps[a].format == MAP_DOOM64)
			format = "doom64";
		else if (maps[a].format == MAP_HEXEN)
			format = "hexen";
		else
			format = "udmf";

		ArchiveEntry* entry = maps[a].head;
		while (entry && entry != maps[a].end->nextEntry())
		{
			entry->exProp("MapFormat") = format;
			entry = entry->nextEntry();
		}
	}

	return maps;
}