Exchanging moho files with intact image links

General Moho topics.

Moderators: Víctor Paredes, Belgarath, slowtiger

User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

I found another bug in the CC_RELINK_LOST_IMAGES.createSubDirList function:

Code: Select all

		batchScript = "~/mohoimpd"
should actually be something like:

Code: Select all

		local homeDir = os.getenv("HOME")
		batchScript = homeDir .. "/mohoimpd"
The tilde (~) thing doesn't work in Lua.

Next, I got this error message:
CC:Relink Lost Images wrote:Image
This probably means the batch file "~/mohoimpd" wasn't executed and therefore the file "mohodlst.dat" wasn't created by the batch script.

So that boils down to the following code not being executed properly:

Code: Select all

	local result = os.execute ( batchScript ) --no chmod??
The Programming in Lua book warns for using os.execute in crossplatform situations:
Programming in Lua wrote:The function os.execute runs a system command; it is equivalent to the system function in C. It receives a string with the command and returns an error code. For instance, both in Unix and in DOS-Windows, you can write the following function to create new directories:

Code: Select all

    function createDir (dirname)
      os.execute("mkdir " .. dirname)
    end
The os.execute function is powerful, but it is also highly system dependent.
Anyway, if I comment out the

Code: Select all

os.remove (batchScript)
I get the following contents of the "~/mohoimpd" file:

Code: Select all

cd "/Users/renevanbelzen/Desktop/relink images/"
ls -d1>mohodlst.dat
(a change directory command to the folder in which the Moho file is located, and a list structure command)

If I execute this file in my BASH CLI, this dialogue appears:

Code: Select all

CP756652-A:~ renevanbelzen$ ./mohoimpd
-bash: ./mohoimpd: Permission denied
So I did the following:

Code: Select all

CP756652-A:~ renevanbelzen$ chmod +x mohoimpd
CP756652-A:~ renevanbelzen$ ./mohoimpd
Now the "mohodlst.dat" file appeared in the same folder as the Moho file, but contained only a period character ".".

And indeed, if you execute

Code: Select all

ls -d1
at the CLI, you only get the period character. I assume you want a complete list of subdirectories over all mounted drives, but this -d1 option obviously isn't the one to use.

I changed the script by inserting the chmod command:

Code: Select all

	if(platform == "unix") then
		local permission = os.execute ( "chmod +x " .. batchScript ) -- chmod
	end
	local result = os.execute ( batchScript ) -- execute
But then, of course, I got another error message:
CC:Relink Lost Images wrote:Image
The code at the end of the CC_RELINK_LOST_IMAGES.createSubDirList function produced this error, probably because the unix command "ls -d1" doesn't do what it is expected to do.

Judging from the name of the function (createSubDirList) I guess the "ls -d1" command should have created a list of all subdirectories over all mounted drives.

Please give me some feedback on this, because I'm really in the dark here. I'm not exactly sure what I'm doing or are supposed to do next.
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Luckily, I got some help from more seasoned Mac users and it seems the debugging I did was useful, because AFAIK the CC:Relink Lost Images script now works on Mac OS X as it should work.

I first tried reading all the subdirectories over all mounted disk with this command:

Code: Select all

find / type = d
However, this took so long, that I aborted this option. Furthermore, if more files with the same name exist, there are going to be serious troubles (because: which file would be the correct one?).

So, I reverted to the ls command in the following form:

Code: Select all

ls -RF1
This lists all files and subdirectories in the following format:

Code: Select all

cross.png
some/
test.moho

./some:
where/

./some/where:
else/

./some/where/else:
rect.png
As you can see, every subdirectory starts with "./" and ends with ":" (excluding the quotes). So, lines that start with "./" and end with ":" are subdirectory are exclusively subdirectories of the currrent path. This means that all other lines should be discarded.

This also means that the procedure of the old version should be modified slightly. The array index counter subDirCount should only be incremented when a line actually contains a subdirectory in the Unix version and every line in the Windows version.

Below are the original and new versions of the CC_RELINK_LOST_IMAGES.createSubDirList function. Please, would you be so kind to try if the new version works on all platforms? It seems to work on Mac OS X.
original version wrote:

Code: Select all

function CC_RELINK_LOST_IMAGES.createSubDirList(path)
	local platform = CC_RELINK_LOST_IMAGES.getOS()
	local cmd = ""
	local subDirList = "mohodlst.dat"
	local batchScript = ""
	if(platform=="win") then
		local drive = string.sub(path, 0 , string.find(path, ":")-1 )
		batchScript = "C:\\mohoimpd.bat"
		cmd = cmd .. drive..":\n"
		cmd = cmd .. "cd ""..path..""\n"		
		cmd = cmd .. "DIR /B /AD /OE > "..subDirList
	else	--unix (osx or linux)
		batchScript = "~/mohoimpd"
		cmd = cmd .. "cd ""..path..""\n"
		cmd = cmd .. "ls -d1>"..subDirList
	end
	local f = io.open (batchScript, "w")
	if (f == nil) then
		print("Error: file write error on " .. batchScript)
		return
	end
	
	f:write(cmd)
	f:close()

	local result = os.execute ( batchScript ) --no chmod??
	os.remove (batchScript)

        local subDirListFile = io.open( path .. subDirList,"r")
	if (subDirListFile == nil) then
		print("Error: file read error on " .. path .. subDirList)
		return
	end
       
        local subDirTable = {}
        local subDirCount = 0
        line = subDirListFile:read()
        while ( line ~= nil ) do
	  if(platform=="win") then
            subDirTable[ subDirCount ] = path .. line .. "\"
          else
            subDirTable[ subDirCount ] = path .. line .. "/"
          end
          subDirCount = subDirCount + 1
          line = subDirListFile:read()
        end
        subDirTable[ subDirCount ] = nil

        subDirListFile:close()
	os.remove (path..subDirList)

        return subDirTable
end
new version wrote:

Code: Select all

function CC_RELINK_LOST_IMAGES.createSubDirList(path)
	local platform = CC_RELINK_LOST_IMAGES.getOS()
	local cmd = ""
	local subDirList = "mohodlst.dat"
	local batchScript = ""
	if(platform=="win") then
		local drive = string.sub(path, 0 , string.find(path, ":")-1 )
		batchScript = "C:\\mohoimpd.bat"
		cmd = cmd .. drive..":\n"
		cmd = cmd .. "cd ""..path..""\n"		
		cmd = cmd .. "DIR /B /AD /OE > "..subDirList
	else	--unix (osx or linux)
		local homeDir = os.getenv("HOME")
		batchScript = homeDir .. "/mohoimpd"
		cmd = cmd .. "cd ""..path..""\n"
		cmd = cmd .. "ls -RF1 > "..subDirList
	end
	local f = io.open (batchScript, "w")
	if (f == nil) then
		print("Error: file write error on " .. batchScript)
		return
	end
	
	f:write(cmd)
	f:close()

	if(platform == "unix") then
		local permission = os.execute ( "chmod +x " .. batchScript ) -- chmod
	end
	local result = os.execute ( batchScript ) -- execute
	os.remove (batchScript)

        local subDirListFile = io.open( path .. subDirList,"r")
	if (subDirListFile == nil) then
		print("Error: file read error on " .. path .. subDirList)
		return
	end
       
	local subDirTable = {}
	local subDirCount = 0
	line = subDirListFile:read()
	while ( line ~= nil ) do
		if(platform=="win") then
			subDirTable[ subDirCount ] = path .. line .. "\"
			subDirCount = subDirCount + 1
		else
			if(string.sub(line,1,2) == "./" and string.sub(line,-1) == ":") then
				local pathName = path .. string.sub(line,3,-2) .. "/"
				subDirTable[ subDirCount ] = pathName
				subDirCount = subDirCount + 1
			end
		end
		line = subDirListFile:read()
	end
	subDirTable[ subDirCount ] = nil

	subDirListFile:close()
	os.remove (path..subDirList)

	return subDirTable
end
Pfew, I hope it works, because I have worked on it for more than eight hours today.
User avatar
7feet
Posts: 840
Joined: Wed Aug 04, 2004 5:45 am
Location: L.I., New Yawk.
Contact:

Post by 7feet »

Oh, the joys of banging your head against a wall to get a script working... But I also instantly saw what might be part of the problem. You quoted a bit of code

Code: Select all

  returnb "unix"
which is a numbskull typo on my part, and I'm sure mucking up the works all over the place. It should just be

Code: Select all

    return "unix"
That one extra letter spells doom, and I should have noticed it. My apologies, with that bit of bollox the function will always return a nil value. In the end and looking over it again, Macton's version shoulda been just fine from the standpoint of my comment, I was just too tired to see it. And I'll try to refrain from jumping into Mac OS problems, as I obviously don't know my elbow from my arse on that subject.

Rasheed, hope that is working right, after your valiant effort, and if it is I'll study it so I can make sure anything else I might do in this vein is properly cross-platform and all that. Good job.
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Thanks, Brian!

I got another tip from a Mac user:
Towel wrote:You can use the "find" command to list all of the files/directories in a given path that meet specified criteria. For example, to list *all* of the directories in *all* mounted volumes (this will be a *very* long list), do:

Code: Select all

find / -type d
Replace "/" with a more specific path to restrict find to looking in that directory. You can also search for a directory with a specific name by adding a "name" criterion (with or without wildcards):

Code: Select all

find /Users/SomeUser/ -type d -name "somename*"
Find's default output is probably exactly the format you want, a simple list of the paths to all matching files/directories, one path per line.
Using this piece of info, I can in effect the same as the Windows CLI command:

Code: Select all

DIR /B /AD /OE
which I suppose lists all subdirectories from the current directory.

The Unix code in my modified script was:

Code: Select all

		cmd = cmd .. "cd ""..path..""\n"
		cmd = cmd .. "ls -RF1 > "..subDirList
and can be converted to:

Code: Select all

		cmd = cmd .. "find ""..path.."" - type d\n"
And the code in my modified script that was:

Code: Select all

	while ( line ~= nil ) do
		if(platform=="win") then
			subDirTable[ subDirCount ] = path .. line .. "\"
			subDirCount = subDirCount + 1
		else
			if(string.sub(line,1,2) == "./" and string.sub(line,-1) == ":") then
				local pathName = path .. string.sub(line,3,-2) .. "/"
				subDirTable[ subDirCount ] = pathName
				subDirCount = subDirCount + 1
			end
		end
		line = subDirListFile:read()
	end
can be reverted to the old code:

Code: Select all

	while ( line ~= nil ) do
		if(platform=="win") then
			subDirTable[ subDirCount ] = path .. line .. "\"
		else
			subDirTable[ subDirCount ] = path .. line .. "/"
		end
		subDirCount = subDirCount + 1
		line = subDirListFile:read()
	end
I will try that and report if it works.
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

I wasn't able to get it to work. All kinds of strange loopings happened when I used "find" and my skills are not good enough to figure out why that is.

Anyway, here is the modification that seems to work (provided you created the environment variable "OS" in MacOSX). I list only the part that has changed from the original:

Code: Select all

--*******************************************************************************************
--Create a list of subdirectories in the chosen directory. Maybe for parsing through
-- a whole directory tree for imports.
--*******************************************************************************************
function CC_RELINK_LOST_IMAGES.createSubDirList(path)
	local platform = CC_RELINK_LOST_IMAGES.getOS()
	local cmd = ""
	local subDirList = "mohodlst.dat"
	local batchScript = ""
	if(platform=="win") then
		local drive = string.sub(path, 0 , string.find(path, ":")-1 )
		batchScript = "C:\\mohoimpd.bat"
		cmd = cmd .. drive..":\n"
		cmd = cmd .. "cd \""..path.."\"\n"		
		cmd = cmd .. "DIR /B /AD /OE > "..subDirList
	else	--unix (osx or linux)
		local homeDir = os.getenv("HOME")
		batchScript = homeDir .. "/mohoimpd"
		cmd = cmd .. "cd \""..path.."\"\n"
		cmd = cmd .. "ls -RF1 > "..subDirList
	end

	local f = io.open (batchScript, "w")
	if (f == nil) then
		print("Error: file write error on " .. batchScript)
		return
	end

	f:write(cmd)
	f:close()

	if(platform == "unix") then
		local permission = os.execute ( "chmod +x " .. batchScript ) -- chmod
	end
	local result = os.execute ( batchScript ) -- execute

	os.remove (batchScript)

	local subDirListFile = io.open( path .. subDirList,"r")
	if (subDirListFile == nil) then
		print("Error: file read error on " .. path .. subDirList)
		return
	end

	local subDirTable = {}
	local subDirCount = 0
	line = subDirListFile:read()
	while ( line ~= nil ) do
		if(platform=="win") then
			subDirTable[ subDirCount ] = path .. line .. "\\"
			subDirCount = subDirCount + 1
		else
			if(string.sub(line,1,2) == "./" and string.sub(line,-1) == ":") then
				local pathName = path .. string.sub(line,3,-2) .. "/"
				subDirTable[ subDirCount ] = pathName
				subDirCount = subDirCount + 1
			end
		end
		line = subDirListFile:read()
	end
	subDirTable[ subDirCount ] = nil

	subDirListFile:close()
	os.remove (path..subDirList)

	return subDirTable
end

--*******************************************************************************************
-- Deterine the system operating system
-- Return "win" or "unix" (as strings)
--*******************************************************************************************
function CC_RELINK_LOST_IMAGES.getOS()
	local opSys = string.lower(string.sub(os.getenv("OS"), 1, 3))
	if opSys == "win" then
		return "win"
	else
		return "unix"
	end
end
If someone wants the modified Lua script, then shoot me a PM with your request and an e-mail adres to send it to.
User avatar
2ner
Posts: 171
Joined: Wed Aug 04, 2004 1:14 pm

Post by 2ner »

We should have a "PACK" and "UNPACK" function like Blender.

Just hit the pack button and all files are saved in the part file. Then you can unpack if you want.

Feature request I guess.
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Rasheed wrote:I wasn't able to get it to work. All kinds of strange loopings happened when I used "find" and my skills are not good enough to figure out why that is.
I think I know where the problem lies. The function CC_RELINK_LOST_IMAGES:FindFileDir calls itself, apparantly to search for the next level of subdirectories. However, Unix already has found the entire subdirectory tree, so there is no need for that in that OS. I expect that the find command brings this algorithm in an endless loop.

It probably means if you want the script to be efficient for both Windows and Unix, you have to write separate scripts. The way Unix handles files and Windows handles files are simply too different if you try anything more complicated than a simple load and write file operation.

I'm sorry if you think the script is a bit slow in MacOSX, but it should work. I don't have access to a Linux machine, so I don't really know if it works under Linux.

It is even possible that such a delicate script as Relink Lost Images should be forked into three separate versions for three separate OSes. I that case, a OS check isn't really necessary anymore, because you simply pick the script that was optimized for your particular OS.

Because of the problems I encountered I have ordered a Unix for MacOSX Panther book, published by O'Reilly. It seems if you are programming in a scripting language like Lua or Python, you really have to understand the underlying operating system (Darwin).

Hopefully the tweaking I did on the CrashCore script for MacOSX will be enough to satify the need for a solution of the original poster:
box wrote:Hi, how can I exchange moho files with other animators so that the links to images remain intact?
Let say there are images which are inside different folders for organisation purposes. I save the moho file above these folders and all is fine. But when I send the moho file to another animator who has exactly the same folders on his harddisk, some of the links get lost, especially when there are subfolders, too.
And because I think my work is done, you can download the ZIP archive containing the script from my webspace:
CC:Relink Lost Images
User avatar
7feet
Posts: 840
Joined: Wed Aug 04, 2004 5:45 am
Location: L.I., New Yawk.
Contact:

Post by 7feet »

Bravo, Rasheed!

The one point I would make is that if you put an additional OS check in the FindFileDir function, and it's Unix, you could bypass the recursion and still keep it one crossplatform script. Or at least I think so, I haven't had a chance to dig around in the script much yet but it seems that should be feasable. I'll look at it first chance.
macton
Posts: 93
Joined: Thu Aug 18, 2005 6:29 am
Location: San Diego

Post by macton »

Great work Rasheed!
And thanks for your contribution too 7feet!

I've integrated the changes.
I've also removed the recursion on OSX and Windows so the script should execute significantly faster on all platforms (and not pop open so many shells under Windows)

The update (v1.2) is here as usual:
http://www.lostmarble.com/forum/viewtopic.php?t=2448
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

There is still a little bug, macton.

Code: Select all

cmd = cmd .. "find \""..path.."\" -type d > " .. path .. subDirList .. "\n"
should actually be

Code: Select all

cmd = cmd .. "find \""..path.."\" -type d > \"" .. path .. subDirList .. "\"\n"
(the second path should be between quotation marks as well).

Other than that it works fine and fast. Thanks!

I should note for MacOSX users that they still need to install the environment variables, either through a property list in "~/.MacOSX/environment.plist" or via the RCEnvironment preference panel and register the key "OS" with value "MacOSX" (anything different from something starting with "win" will suffice).

For who want to know, this is because of the

Code: Select all

os.getenv("HOME")
snippet. It is strange that the "~" doesn't seem to work. I will investigate why that is and what can be done about it, other than using the environment.plist trick.
macton
Posts: 93
Joined: Thu Aug 18, 2005 6:29 am
Location: San Diego

Post by macton »

Rasheed wrote:There is still a little bug, macton.
Good catch. The script has been updated.
Rasheed wrote: I should note for MacOSX users that they still need to install the environment variables, either through a property list in "~/.MacOSX/environment.plist" or via the RCEnvironment preference panel and register the key "OS" with value "MacOSX" (anything different from something starting with "win" will suffice).
Actually, this shouldn't be necessary now. If "OS" is not set at all the script now just assumes "unix". This works on my OSX box - is it not working for you?
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

macton wrote:
Rasheed wrote:]
I should note for MacOSX users that they still need to install the environment variables, either through a property list in "~/.MacOSX/environment.plist" or via the RCEnvironment preference panel and register the key "OS" with value "MacOSX" (anything different from something starting with "win" will suffice).
Actually, this shouldn't be necessary now. If "OS" is not set at all the script now just assumes "unix". This works on my OSX box - is it not working for you?
:oops: I didn't really try this out before I posted this about RCEnvironment. I removed the .MacOSX folder, logged out and logged in again (.MacOSX folder still removed) and the script kept working. This is a good lesson for the future: don't assume something works or doesn't work.

So:
Rasheed wrote:You don't need RCEnvironment for this script to work in MacOSX.
But I still want to know why the tilde as a reference to your home directory doesn't work, just out of curiosity. Perhaps I should look into it and find some examples that should work in theory, but don't work when you run them, and post those along with a question in some Lua scripting forum. It could be, that I've made a mistake, hence the examples. I don't want to bother forum posters more than necessary.
User avatar
Rasheed
Posts: 2008
Joined: Tue May 17, 2005 8:30 am
Location: The Netherlands

Post by Rasheed »

Rasheed wrote:But I still want to know why the tilde as a reference to your home directory doesn't work, just out of curiosity. Perhaps I should look into it and find some examples that should work in theory, but don't work when you run them, and post those along with a question in some Lua scripting forum. It could be, that I've made a mistake, hence the examples. I don't want to bother forum posters more than necessary.
Well, I didn't have to bother others with my Mac specific problem. I found the answer myself.

The operating system needs the following file description for a file with spaces in its pathname and the tilde character as an abbreviation for the home directory:

Code: Select all

~/"some directory with spaces/some file.txt"
or with single quotes:

Code: Select all

~/'some directory with spaces/some file.txt'
Lua, however, needs a completed directory path for its open function and doesn't seem to be able to handle the tilde "~" abbreviation for the home directory. I have tried several combinations to no avail.

Here is the Unix-specific Lua script I tested (with the stand-alone Lua interpreter, but it can also be tested by embedding it as a script in a Moho layer):

Code: Select all

function openFile(pathName)
	local f = io.open(pathName, "w")
	if ( f == nil ) then
		print( "IO Error: "..pathName )
	else
		f:close()
	end
end

openFile("~/file_1.txt")
openFile('~/file_2.txt')
openFile("~/'file_3.txt'")
openFile('~/"file_4.txt"')
openFile("users/yourusername/file_5.txt")
(replace "yourusername" by your actual user name) and this was the output in my BASH CLI:

Code: Select all

IO Error: ~/file_1.txt
IO Error: ~/file_2.txt
IO Error: ~/'file_3.txt'
IO Error: ~/"file_4.txt"
It also resulted in the creation of the file "file_5.txt" in my home directory.

So there you have it. You need to convert your leading tilde character and forward slash into a home directory path for it to work with the Lua open function. Of course, the home directory path is defined by

Code: Select all

os.getenv("HOME")
And when you come to think of it, it actually makes sense not to include such a feature, because the "~" is platform-specific and you don't want that to happen in a (as nearly as possible) platform-independent scripting language.
User avatar
box
Posts: 44
Joined: Sat Nov 12, 2005 3:46 pm
Contact:

Post by box »

Cheers Rasheed and Macton, thanks a lot for your effort.
I tried it and now it works like a miracle. Its nearly as good as being well organized. :wink:

Errr... is it possible that all of the CC scripts involving file handling (ie. "load layer animation") cause similar errors? I never could get these scripts running on OS X.

(Suggestion to admin: Maybe this thread should better be moved in the scripting section.)
grosbouff
Posts: 16
Joined: Mon Dec 18, 2006 1:53 pm

Post by grosbouff »

OK; the link to the script
http://www.lowrestv.com/moho_stuff/scri ... cripts.zip
is broken.

Anyway, the good thing to know if that your graphic elements are "under" the .anme file (on the same "level" or in subdirectories), the paths are relatives. Elsewhere it will be absolute paths, but that you can correct by editing your .anme file and replacing all the occurences :
for example;
E:/work2008/DH/magsport/emt/CD DH footballeurs/Personnages/footbaleur/man02/brasG.png will be elements/persos/footbaleur/man02/brasG.png
So you just need to use "search and replace", and replace (here)
E:/work2008/DH/magsport/emt/CD DH footballeurs/Personnages/ by elements/persos/ !
Good to know...
Post Reply