Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Means of updating Starfield's .mesh paths in bulk #76

Closed
JMPZ11 opened this issue Apr 6, 2024 · 6 comments
Closed

Comments

@JMPZ11
Copy link

JMPZ11 commented Apr 6, 2024

Need a means of bulk updating mesh paths in a Starfield.nif.

Thanks to the recursive export feature mentioned in #75, I am able to use the blender plugin to import the entire nif. This dramatically simplified the process of punching holes (and as a bonus I got all the LODSs as well) but... the .nif file it creates is just all messed up (it seems targetted toward outfits and armor rather than ship modules). The meshes, however, seem fine.

I made a little batch script to move / rename all the exported meshes back to the vanilla paths so I could use it with the original .nif.

It works like a charm... except I can't leave them in the vanilla paths - it would override the source object. And I don't really have as way to tell which meshes were changed... so need them all probably.

At the most basic level, just having the ability to add a prefix or suffix without renaming would at least make it possible. Preferably something like regex replace, or even better - rename them all to be in a single folder with more meaningful names.

Another alternative would be if I could somehow copy the paths from the plugin output nif onto the original..

(yet another would be if the .nif could be exported to xml to do some regex replace and then imported?

This while file pointer to meshes business really is pain isn't it...

Thank you so much for all of this. Truly amazing work.

@JMPZ11
Copy link
Author

JMPZ11 commented Apr 6, 2024

I'm taking a stab at hacking in my own hardcoded spell - I'm no c++ guy so hacking is the operative word lol

@JMPZ11
Copy link
Author

JMPZ11 commented Apr 6, 2024

I used this to rename and everything looks fine in nifskope, but the model is missing in Starfield...

#include "spellbook.h"

#include <QDialog>
#include <QFileDialog>
#include <QSettings>

#include "gamemanager.h"
#include "libfo76utils/src/common.hpp"
#include "libfo76utils/src/filebuf.hpp"
#include "libfo76utils/src/material.hpp"

#ifdef Q_OS_WIN32
#  include <direct.h>
#else
#  include <sys/stat.h>
#endif

// Brief description is deliberately not autolinked to class Spell
/*! \file filerename.cpp
 * \brief Prepend "example" folder to all meshes (spRenameAllResources)
 *
 * All classes here inherit from the Spell class.
 */

//! 
class spRenameAllResources final : public Spell
{
public:
	QString name() const override final { return Spell::tr( "Add example prefix to all meshes"); }
	QString page() const override final { return Spell::tr( "" ); }
	QIcon icon() const override final
	{
		return QIcon();
	}
	bool constant() const override final { return true; }
	bool instant() const override final { return true; }

	bool isApplicable( const NifModel * nif, const QModelIndex & index ) override final
	{
		return ( nif && !index.isValid() );
	}

	static bool is_Applicable2( const NifModel * nif, NifItem * item )
	{
		NifValue::Type	vt = item->valueType();
		if ( vt != NifValue::tStringIndex && vt != NifValue::tSizedString ) {
			if ( !( nif->checkVersion( 0x14010003, 0 ) && ( vt == NifValue::tString || vt == NifValue::tFilePath ) ) )
				return false;
		}
		do {
			if ( item->parent() && nif && nif->getBSVersion() >= 130 ) {
				if ( item->name() == "Name" && ( item->parent()->name() == "BSLightingShaderProperty" || item->parent()->name() == "BSEffectShaderProperty" ) )
					break;		// Fallout 4, 76 or Starfield material
			}
			if ( item->parent() && item->parent()->name() == "Textures" )
				break;
			if ( item->name() == "Path" || item->name() == "Mesh Path" || item->name().startsWith( "Texture " ) )
				break;
			return false;
		} while ( false );
		return !( nif->resolveString( item ).isEmpty() );
	}

	bool updateNifItemFilePath( NifModel * nif, NifItem * item, std::string prefix );
	void renameFiles( NifModel * nif, NifItem * item, std::string prefix );
	QModelIndex cast( NifModel * nif, const QModelIndex & index ) override final;
};


bool spRenameAllResources::updateNifItemFilePath( NifModel * nif, NifItem * item, std::string prefix)
{
	quint32	bsVersion = nif->getBSVersion();
	if ( bsVersion >= 160 && item->name() == "Mesh Path" ) {
		QString	filePath( nif->resolveString( item ) );
		if ( filePath.isEmpty() )
			return false;
		nif->assignString(item, QString::fromStdString(prefix) + filePath, true);
		return true;
	}

return false;
}

//recursive
void spRenameAllResources::renameFiles(NifModel * nif, NifItem * item, std::string prefix )
{
	//processs current node
	if ( spRenameAllResources::is_Applicable2( nif, item ) ) {
		spRenameAllResources::updateNifItemFilePath( nif, item, prefix );
	}
	// process children
	for ( int i = 0; i < item->childCount(); i++ ) {
		if ( item->child( i ) )
			renameFiles(nif, item->child( i ) , prefix);
	}
}

QModelIndex spRenameAllResources::cast( NifModel * nif, const QModelIndex & index )
{
	if ( !nif )
		return index;
	Game::GameMode	game = Game::GameManager::get_game( nif->getVersionNumber(), nif->getUserVersion(), nif->getBSVersion() );

	std::set< std::string >	fileSet;
	for ( int b = 0; b < nif->getBlockCount(); b++ ) {
		NifItem * item = nif->getBlockItem( quint32(b) );
		if ( item )
		// THIS IS WHERE YOU CHANGE PREFIX
			renameFiles(nif, item, "example\\");
	}
	return index;
}

REGISTER_SPELL( spRenameAllResources )


@fo76utils
Copy link

I will look into implementing this feature, it would not be difficult to use regular expression search/replace, since QString already has a function that implements that.

@fo76utils
Copy link

A spell for this purpose is now added, it can be found in the main menu as "Search/Replace Resource Paths". It takes the following as input:

  • Regular expression to search for: the pattern to be replaced, note that characters like \ need to be escaped.
  • Replacement text: this is what every match of the above will be replaced with. It may reference groups defined with parentheses in the regular expression, using \1, \2, etc. (see the example here).
  • Path filter regular expression: only paths matching this are processed. If the field is left empty, then it matches everything.

All replacements need to be confirmed in a message box.

@JMPZ11
Copy link
Author

JMPZ11 commented Apr 10, 2024 via email

@JMPZ11
Copy link
Author

JMPZ11 commented May 12, 2024

This is great, thank you - sorry for the delayed response. Closing!

@JMPZ11 JMPZ11 closed this as completed May 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants