<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>/dev/klog &#187; Code</title>
	<atom:link href="http://www.devklog.net/category/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.devklog.net</link>
	<description>You better pipe that through your mind</description>
	<pubDate>Thu, 26 Jun 2008 17:11:21 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Improving the Xcode sub-project experience with scripts and wits</title>
		<link>http://www.devklog.net/2007/10/07/improving-the-xcode-sub-project-experience-with-scripts-and-wits/</link>
		<comments>http://www.devklog.net/2007/10/07/improving-the-xcode-sub-project-experience-with-scripts-and-wits/#comments</comments>
		<pubDate>Sun, 07 Oct 2007 04:08:02 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[script]]></category>

		<category><![CDATA[xcode]]></category>

		<guid isPermaLink="false">http://www.devklog.net/2007/10/07/improving-the-xcode-sub-project-experience-with-scripts-and-wits/</guid>
		<description><![CDATA[There are a few annoying bugs when using sub-projects in Xcode that build dependencies for the &#8220;master&#8221; project, particularly for frameworks or shared libraries that have header files and that need to be embedded in the final product (built by the &#8220;master&#8221; project). This is the case for MHKKit and Riven X.
My solution to those [...]]]></description>
			<content:encoded><![CDATA[<p>There are a few annoying bugs when using sub-projects in Xcode that build dependencies for the &#8220;master&#8221; project, particularly for frameworks or shared libraries that have header files and that need to be embedded in the final product (built by the &#8220;master&#8221; project). This is the case for MHKKit and Riven X.</p>
<p>My solution to those problems has been for a while to set a common build directory for my entire user. This preference is set in the Xcode preferences. Having a single build directory has quite a number of advantages (one-shot delete of temporary build files, Spotlight ignore for faster build times, one directory to go to for Terminal sessions, one directory to add to dyld environment variables, etc). It also happens to fix all sub-project related issues, because every project suddenly has the same build directory.</p>
<p>However, a project should not assume that this will be the case. Most external contributors will not have set that Xcode preferences, or they may have a good reason for not doing so. In any case, a project should always build successfully no matter the specific environment, given a sane Xcode tools installation.</p>
<p>So let&#8217;s look at some of the more annoying issues, and how we can resolve them in a manner that works in both situations (common build directory or not).</p>
<p><strong>Framework header files</strong></p>
<p>Despite the sub-project properly added to the master project, and even though linking phases will properly resolve the path of any shared library or framework reference from an sub-project, including framework header files will fail. This comes as a bit of surprise, because adding a framework as a regular project item, no matter where it is on disk, will allow framework includes to work for that particular framework.</p>
<p>The solution to this problem is of course to specify the sub-project&#8217;s build directory in the framework search path. An additional twist is to use Xcode build setting variables to handle all configurations at once. One does need to assume certain things about the sub-project&#8217;s location and build settings, but for the common case of having the sub-project in a sub-directory below the &#8220;master&#8221; project, my solution will do the job.</p>
<p>The generic pattern is something like this:</p>
<pre><code>FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/SUB-PROJECT-DIRECTORY-NAME/build/$(CONFIGURATION)";</code></pre>
<p>where SUB-PROJECT-DIRECTORY-NAME is the name of the sub-directory containing the sub-project. As an example, Riven X&#8217;s Base.xcconfig contains the following:</p>
<pre><code>FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/mhk/build/$(CONFIGURATION)";</code></pre>
<p>Although I have no experience with shared library include files, I also strongly suspect including them will fail. Their case is a bit different, considering the header files will not be bundled, but the gist of the problem remains the same. Adapting my solution for frameworks to shared library is a (trivial) exercise for the reader.</p>
<p><strong>Copying sub-project build products</strong></p>
<p>For some unknown reason (and most likely not a very good one), using a sub-project build product reference in a copy phase in the &#8220;master&#8221; project will fail. The Xcode build system construct an incorrect path, assuming the referenced build product is in the &#8220;master&#8221; project&#8217;s build directory.</p>
<p>The solution is to use a shell script to copy that build product. As an additional requirement, I wanted to use Xcode&#8217;s copy tool (pbxcp) in exactly the same way as the build system does. The script makes the same assumption as the solution for framework header files.</p>
<p>First, the code. I&#8217;m not going to make it generic, so this is for copying MHKKit to the Riven X bundle. You can of course copy more than one item per script invocation with proper modifications. On with the code:</p>
<pre><code># Find the pbxcp tool
COPY_TOOL="${DEVELOPER_LIBRARY_DIR}/PrivateFrameworks/DevToolsCore.framework/Resources/pbxcp"
if [ ! -f "${COPY_TOOL}" ]; then
	COPY_TOOL=&#8221;/System/Library/PrivateFrameworks/DevToolsCore.framework/Resources/pbxcp&#8221;
fi

# Find the build product
MHKKIT_PATH=&#8221;${BUILT_PRODUCTS_DIR}/MHKKit.framework&#8221;
if [ ! -d "${MHKKIT_PATH}" ]; then
	# assume default Xcode setup where MHKKit was built in its own build directory
	MHKKIT_PATH=&#8221;${SRCROOT}/mhk/build/${CONFIGURATION}/MHKKit.framework&#8221;
fi

# Copy the build product
$COPY_TOOL -exclude .DS_Store -exclude CVS -exclude .svn -resolve-src-symlinks &#8220;${MHKKIT_PATH}&#8221; &#8220;${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}&#8221;
</code></pre>
<p>The script first finds pbxcp (in a way that will work on both 10.4 with Xcode 2.4 and above, and 10.5 with Xcode 2.5 and 3.0). It then assumes the build product (in this case, &#8220;MHKKit.framework&#8221;) is in the &#8220;master&#8221; project build directory. If that turns out not to be the case, it assumes it is in the default build directory of the sub-project (in this case, the MHKKit project) for the active configuration. It finally invokes pbxcp to copy the build product to the appropriate location (in this case, the &#8220;Frameworks&#8221; directory of the Riven X application bundle).</p>
<p>For a list of build setting variables for common bundle locations, see <a href="http://developer.apple.com/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/index.html" title="Xcode Build Setting Reference" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">Xcode Build Setting Reference</a>, specifically the &#8220;Product Layout Build Settings&#8221; category.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2007/10/07/improving-the-xcode-sub-project-experience-with-scripts-and-wits/feed/</wfw:commentRss>
		</item>
		<item>
		<title>NSNotificationCenter isn&#8217;t a singleton</title>
		<link>http://www.devklog.net/2007/04/26/nsnotificationcenter-isnt-a-singleton/</link>
		<comments>http://www.devklog.net/2007/04/26/nsnotificationcenter-isnt-a-singleton/#comments</comments>
		<pubDate>Thu, 26 Apr 2007 21:16:42 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.devklog.net/2007/04/26/nsnotificationcenter-isnt-a-singleton/</guid>
		<description><![CDATA[Quoting from a reaction to Brent Simmons&#8217;s recent entry on large Cocoa projects:
As noted by Michael Tsai, NSNotificationCenter is a singleton, and thus essentially is one big honkin’ global. Globals are bad ™.
This was posted by Andy Finnell echoing Michael Tsai.
But in fact, NSNotificationCenter isn&#8217;t a singleton. You can create as many as you want. [...]]]></description>
			<content:encoded><![CDATA[<p>Quoting from a reaction to <a href="http://inessential.com/2007/04/25.php" onclick="javascript:pageTracker._trackPageview ('/outbound/inessential.com');">Brent Simmons&#8217;s recent entry on large Cocoa projects</a>:</p>
<blockquote><p>As noted by Michael Tsai, NSNotificationCenter is a singleton, and thus essentially is one big honkin’ global. Globals are bad ™.</p></blockquote>
<p><a href="http://www.losingfight.com/blog/2007/04/26/in-defense-of-observers/" onclick="javascript:pageTracker._trackPageview ('/outbound/www.losingfight.com');">This was posted by Andy Finnell</a> echoing <a href="http://mjtsai.com/blog/2007/04/25/large-cocoa-projects" onclick="javascript:pageTracker._trackPageview ('/outbound/mjtsai.com');">Michael Tsai</a>.</p>
<p>But in fact, NSNotificationCenter isn&#8217;t a singleton. You can create as many as you want. I agree that sending every notification to the default center isn&#8217;t a good idea. It turns it into a monolithic entity it isn&#8217;t meant to be. The default notification center should be kept only for communication with the rest of Foundation and AppKit. </p>
<p>However, if you are careful in your designs, you can easily have sub-system notification centers which only certain class clusters know about. That way, you get the benefits of NSNotificationCenter without the problems that have been underlined by several people already.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2007/04/26/nsnotificationcenter-isnt-a-singleton/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Macros for more sane NSError programming</title>
		<link>http://www.devklog.net/2007/04/01/macros-for-more-sane-nserror-programming/</link>
		<comments>http://www.devklog.net/2007/04/01/macros-for-more-sane-nserror-programming/#comments</comments>
		<pubDate>Sun, 01 Apr 2007 17:14:47 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.devklog.net/2007/04/01/macros-for-more-sane-nserror-programming/</guid>
		<description><![CDATA[Jeff Johnson recently posted an entry about using NSError in Cocoa to dramatically improve error handling. I wholeheartedly agree with the idea, and in fact adding NSError &#8220;variants&#8221; during MPQKit&#8217;s refactor was one of my top priorities.
That being said, there are a few annoyances with using NSError, one of which is constantly having to check [...]]]></description>
			<content:encoded><![CDATA[<p>Jeff Johnson recently <a href="http://lapcatsoftware.com/blog/2007/04/01/invoking-errors/" onclick="javascript:pageTracker._trackPageview ('/outbound/lapcatsoftware.com');">posted an entry about using NSError in Cocoa to dramatically improve error handling</a>. I wholeheartedly agree with the idea, and in fact adding NSError &#8220;variants&#8221; during MPQKit&#8217;s refactor was one of my top priorities.</p>
<p>That being said, there are a few annoyances with using NSError, one of which is constantly having to check if the NSError ** variable your method was given isn&#8217;t nil before you dereference it to assign it a new NSError object. There&#8217;s also the verbosity of creating those instances.</p>
<p>So in order to make my life a little bit easier, I wrote a set of macros that cover the specific case where you want to create an NSError instance and then bail out of the method. They check if the NSError ** is nil or not, create an NSError instance and assign it to the reference (or set the reference to nil for the no error ones), then return a provided value to the caller. It&#8217;s important to note that you shouldn&#8217;t use a semi-colon after them, since the macros expand to a block ({&#8230;}). This is necessary for the macros to work correctly in situations such as if statements.</p>
<p>You can <a href="http://zohar.devklog.net:47805/projects/mpqkit/browser/PHSErrorMacros.h" onclick="javascript:pageTracker._trackPageview ('/outbound/zohar.devklog.net:47805');">download the header file from the MPQKit Trac wiki</a>. There are no license restrictions whatsoever.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2007/04/01/macros-for-more-sane-nserror-programming/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Snake eats moon</title>
		<link>http://www.devklog.net/2006/03/14/snake-eats-moon/</link>
		<comments>http://www.devklog.net/2006/03/14/snake-eats-moon/#comments</comments>
		<pubDate>Wed, 15 Mar 2006 00:19:59 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.devklog.net/?p=39</guid>
		<description><![CDATA[The wowsv Python module exports a single function, aptly named wowsv, to load World of Warcraft saved variables Lua files into Python.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working on my spare&#8230; spare time on some web software for my <a href="http://www.worldofwarcraft.com" title="World of Warcraft" onclick="javascript:pageTracker._trackPageview ('/outbound/www.worldofwarcraft.com');">World of Warcraft</a> <a href="http://www.alexstraza.net" title="Alexstraza Dragon Riders" onclick="javascript:pageTracker._trackPageview ('/outbound/www.alexstraza.net');">guild</a>, principally to offer DKP statistics and character profiles.</p>
<p>I originally anticipated to do that stuff in PHP, but I figured it would be nice to learn something new, so I finally settled on <a href="http://www.djangoproject.com/" title="Django" onclick="javascript:pageTracker._trackPageview ('/outbound/www.djangoproject.com');">Django, the “Web framework for perfectionists with deadlines”</a>. Django is written in <a href="http://www.python.org/" title="Python" onclick="javascript:pageTracker._trackPageview ('/outbound/www.python.org');">Python</a>, which is a really nice language, not to mention hip at geek parties.</p>
<p>In any case, it hit me last weekend I would have to load World of Warcraft saved variables files, which are just <a href="http://www.lua.org/" title="The Programming Language Lua" onclick="javascript:pageTracker._trackPageview ('/outbound/www.lua.org');">Lua files</a> declaring variables in the global scope. I thought this would be an ordeal and didn&#8217;t look forward to writing parsing code, or worse, a formal grammar.</p>
<p>Turns out Lua is so similar to Python a few regular expressions did the trick. I have to admit, I was rather pleased by the simplicity of the solution <img src='http://www.devklog.net/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>So there you have it: the wowsv Python module. It exports a single function, aptly named wowsv, to load World of Warcraft saved variables into Python. You can do whatever you want with it, I don&#8217;t really care. Just credit me if you&#8217;re going to use it, and of course I&#8217;d appreciate to know about any changes or improvements you might make to it.</p>
<p><a href="http://www.devklog.net/wordpress/wp-content/wowsv.zip" onclick="javascript:pageTracker._trackPageview ('/downloads/zip/wowsv.zip');">Download wowsv</a> (sha1: 4711ce5a69e774b973bc5de2975051e18da11ee5)</p>
<p>In case you were wondering about the compression, my server would try to execute a straight .py as a CGI script.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2006/03/14/snake-eats-moon/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Where is my playable Riven X, part II</title>
		<link>http://www.devklog.net/2006/02/03/where-is-my-playable-riven-x-part-ii/</link>
		<comments>http://www.devklog.net/2006/02/03/where-is-my-playable-riven-x-part-ii/#comments</comments>
		<pubDate>Sat, 04 Feb 2006 00:56:15 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[Projects]]></category>

		<guid isPermaLink="false">http://www.devklog.net/?p=37</guid>
		<description><![CDATA[That implies computing a new depth value whenever an element is enabled, and eventually a complete re-quantization of all enabled elements when you reach the upper bound of the dynamic range of your depth values.Finally, depth testing doesn't solve the what to render problem entirely.  Indeed, depth testing can be used to filter out “too far” objects, but you need to define your “far plane” or “depth clipping value” and initially set the depth value of all your primitives to that cutoff threshold.The texture problemIf you recall from the previous entry, one of the big advantages of depth testing is the ability to render a large number of primitives in one OpenGL operation (using vertex arrays and the likes of glDrawArray) that share common state properties....  Typically, a 3D model will be made of a few sub-sections, each of which is made of n primitives and one texture (or a few with multitexture) that will be applied to all the primitives using each primitive's texture coordinates.]]></description>
			<content:encoded><![CDATA[<p>In my previous entry, I discussed the requirements of card rendering in Riven X as well as using depth testing to satisfy those requirements. I concluded by mentionning depth testing had its own problems and that I would further detail my design process later. So let&#8217;s get on with it, shall we?</p>
<p><strong>Why depth testing doesn&#8217;t cure cancer</strong></p>
<p>Depth testing can be extremely useful in 3D applications, but Riven X is only a simple 2D compositing engine. As such, depth testing isn&#8217;t required to obtain the desired image but only as an optimization technique. So for depth testing to make sense, it needs to offer a net advantage over other methods, which is far from being obvious.</p>
<p>First of all, depth testing has a memory cost. Not only must you maintain a set of z coordinates for all your primitives, you must also instruct OpenGL to maintain a depth buffer as part of its framebuffer. It is true that statistically there will never be a large number of primitives in a given card, however it is also likely Riven X will need to send large textures to the GPU each frame (such as for movies). Consequently, memory bandwidth to the GPU becomes a precious commodity that needs to be managed tightly.</p>
<p>Secondly, depth testing adds a level of complexity that other more naïve solutions do not have. Specifically, because Riven X is a 2D composite, there is no depth information in a card&#8217;s description. As such, it would be necessary to generate z coordinates for each renderable element before depth testing could be used to control display order. This would essentially boil down to a simple quantization problem (m elements to render, n possible depth values). Nothing to worry about but why do more when you can do less? In addition, elements can be enabled and disabled. That implies computing a new depth value whenever an element is enabled, and eventually a complete re-quantization of all enabled elements when you reach the upper bound of the dynamic range of your depth values.</p>
<p>Finally, depth testing doesn&#8217;t solve the <em>what to render</em> problem entirely. Indeed, depth testing can be used to filter out “too far” objects, but you need to define your “far plane” or “depth clipping value”  and initially set the depth value of all your primitives to that cutoff threshold.</p>
<p><strong>The texture problem</strong></p>
<p>If you recall from the previous entry, one of the big advantages of depth testing is the ability to render a large number of primitives in one OpenGL operation (using vertex arrays and the likes of <em>glDrawArray</em>) that share common state properties. This is the typical case of 3D applications, where models are aggregates of various types of primitives (triangles, strips, quads, etc). However, this is not applicable to Riven X because each primitive is essentially used as a billboard on which a unique texture can be applied. Typically, a 3D model will be made of a few sub-sections, each of which is made of n primitives and one texture (or a few with multitexture) that will be applied to all the primitives using each primitive&#8217;s texture coordinates. If I were to do the same in Riven X, I&#8217;d have to dynamically generate a large composite texture with each element&#8217;s texture in it. Because each of those sub-regions will be rectangular, it is likely I&#8217;d run out of texture space rapidly. And that doesn&#8217;t include the fact I&#8217;d have to write code to do the composite in an optimal fashion. Thermodynamics win, solution dismissed, I remain in my lazy state.</p>
<p><strong>A simple array</strong></p>
<p>Riven X uses a simple <em>enabled elements</em> array to drive the card rendering loop, which looks like this:</p>
<p><code>	NSEnumerator *renderListEnumerator = [_renderList objectEnumerator];<br />
	id anObject = nil;<br />
	Class lastRenderableClass = nil;</p>
<p>	while(anObject = [renderListEnumerator nextObject]) {<br />
		if([anObject isKindOfClass:pictureClass]) {<br />
			unsigned short pictureIndex = [(NSNumber *)anObject unsignedShortValue];</p>
<p>			// bind the pictures VBO and arrays only when we must<br />
			if(lastRenderableClass != pictureClass) {<br />
				lastRenderableClass = pictureClass;</p>
<p>				glBindBuffer(GL_ARRAY_BUFFER, _pictureCoordsVBO);<br />
				glVertexPointer(2, GL_FLOAT, 0, BUFFER_OFFSET(0));<br />
				glTexCoordPointer(2, GL_FLOAT, 0, BUFFER_OFFSET(_pictureTexCoordsOffset));<br />
			}</p>
<p>			// bind the picture texture and draw the quad<br />
			glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _pictureTextureObjects[pictureIndex]);<br />
			glDrawArrays(GL_QUADS, pictureIndex, 4);<br />
		}<br />
	}</code></p>
<p>It uses the class of each element to know how to render it (right now, I&#8217;m only rendering pictures) and tries to minimize state changes as much as possible. An array is a relatively simple solution that doesn&#8217;t offer fast deactivation but relatively OK activation. It should be noted that deactivation wouldn&#8217;t be fast either if I was using depth testing (both are linear), which may be fixed in both instances using a reference table from element resource ID to element index.</p>
<p>However, sampling indicates Riven X renders a lot more often than it enables or disables elements, so this isn&#8217;t a significant problem.</p>
<p>So this is how it works for now, and until I have a more complete application that I can sample in a more realistic manner, an array will do just fine.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2006/02/03/where-is-my-playable-riven-x-part-ii/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Pay heed to the Red Sweater</title>
		<link>http://www.devklog.net/2006/02/01/pay-heed-to-the-red-sweater/</link>
		<comments>http://www.devklog.net/2006/02/01/pay-heed-to-the-red-sweater/#comments</comments>
		<pubDate>Wed, 01 Feb 2006 23:46:18 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.devklog.net/?p=36</guid>
		<description><![CDATA[Daniel Jalkut of Red Sweater Software fame wrote an entry today about warnings and how we dutifully ignore them.  I share his concerns wholeheartedly.  There is nothing more frustrating than compiling a project (of seemingly good quality on the web) only to be bombarded with all manners of warnings to the point the important stuff just slips by unnoticed.]]></description>
			<content:encoded><![CDATA[<p>Daniel Jalkut of Red Sweater Software fame <a href="http://www.red-sweater.com/blog/95/a-word-of-caution" title="A Word of Caution" onclick="javascript:pageTracker._trackPageview ('/outbound/www.red-sweater.com');">wrote an entry today about warnings and how we dutifully ignore them</a>. I share his concerns wholeheartedly. There is nothing more frustrating than compiling a project (of seemingly good quality on the web) only to be <em>bombarded</em> with all manners of warnings to the point the important stuff just slips by unnoticed. Objective-C makes this all the more easier due to its dynamic nature.</p>
<p>Although I don&#8217;t believe in forcing warnings into errors (some warnings can legitimately be ignored), I do make it a point to kill as many warnings (if not all of them) in code I ship (milestones, releases). Obviously, warnings will be abundant during development, but it should be just as important to get them solved as it is to debug and sample before pushing a program to the public.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2006/02/01/pay-heed-to-the-red-sweater/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Where is my playable Riven X?</title>
		<link>http://www.devklog.net/2005/11/30/where-is-my-playable-riven-x/</link>
		<comments>http://www.devklog.net/2005/11/30/where-is-my-playable-riven-x/#comments</comments>
		<pubDate>Thu, 01 Dec 2005 03:31:42 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[Projects]]></category>

		<guid isPermaLink="false">http://www.devklog.net/?p=25</guid>
		<description><![CDATA[If one reads the Riven X project page, one can read “but I expect something barely playable in November”. Obviously, that's not going to happen. Without going into boring details, let's just say it's been one hell of a semester.]]></description>
			<content:encoded><![CDATA[<p><strong>Update: </strong><a href="http://www.devklog.net/2006/02/03/where-is-my-playable-riven-x-part-ii/" title="Where is my playable Riven X, part II">Part II is now available</a>.</p>
<p>If one reads the Riven X project page, one can read “but I expect something barely playable in November”. Obviously, that&#8217;s not going to happen. Without going into boring details, let&#8217;s just say it&#8217;s been one hell of a semester.</p>
<p>So I thought I&#8217;d discuss a particular issue that&#8217;s been annoying me with Riven X: how to render a card. Specifically, how to efficiently render what needs to be rendered in a way that will yield the proper display order while allowing reasonably fast object rendering activation and deactivation.</p>
<p><strong>Background theory</strong></p>
<p>Essentially, Riven is made of stacks and cards. Just like in real-life, a stack contains multiple cards. But what does a card contain? Essentially, renderable objects: pictures, movies, sound effects. Obviously, sound effects are completely different from pictures and movies because they are strictly acoustic in nature. That leaves us to deal with pictures and movies as far as graphics are concerned. The biggest problem by far is to respect the display order. You see, initially a card is blank: it has no active renderable objects. But as scripts are executed in reaction to game state changes or user events, renderable objects start getting activated and deactivated. The rule of the game is then simple: whatever gets activated last is drawn on top of everything. In other words, you “pile things up” as you activate them. Deactivating an item means removing it from the stack while preserving the order of the other elements. We can make a simple diagram to illustrate this:</p>
<p><img src="http://www.devklog.net/wordpress/wp-content/card.png" height="413" width="382" border="0" align="middle" hspace="4" vspace="4" alt="card diagram" title="card diagram" /></p>
<p>This seemingly simple problem has caused me more  than one headache, and I&#8217;m still unsure the solution I settled upon will be satisfactory. Only sampling will tell, and I&#8217;m far from being able to do that. At this point, you may continue reading for details about potential solutions, or wait for my next entry which should be concerned with the solution I have chosen and the unavoidable reasons supporting that choice.</p>
<p><span id="more-25"></span></p>
<p><strong>How are card objects stored?</strong></p>
<p>This may not be final, but the current organization of data in cards is presently as follows:</p>
<ol>
<li>A single <em>vertex buffer object</em> (VBO) for all the picture coordinates. This includes 4 vertex coordinates (one for every vertex defining the region of a picture, or the <em>quad</em> in OpenGL parlance) and 4 texture coordinates (essentially, a texture coordinate is a 2 component vector (for 2D textures anyways) that maps a particular point in a texture (in normalized coordinates – -1 to 1) to a particular vertex) for each picture.</li>
<li>An array of <em>texture objects</em>, one for each picture. The order of this array matches the order in the coordinates VBO.</li>
<li>An array of GLMovie objects, each of them using one VBO for vertex coordinates, one VBO for texture coordinates and one texture object.</li>
</ol>
<p><strong>How does storage affect rendering?</strong></p>
<p>There are quite a large number of factors we could enumerate here, but the principal one has to do with minimizing <em>state changes</em>. OpenGL is essentially a large <em>state machine</em> (this is somewhat less true with the <em>OpenGL Shading Language</em>, but we&#8217;ll overlook that), and everytime you change the state, there is an associated cost. Some operations are more costly than others, but the general rule is <em>to render such that state changes are minimized</em>. Of course, one must also get accurate rendering, so not only must you respect the former rule, you need to do so in a way that <em>preserves display order</em>.</p>
<p>With that in mind, it should be relatively clear that in the case of a card, we&#8217;d like to bind (make current, activate, select) the picture VBO, render all enabled pictures (one at a time unfortunately, since we need to bind the correct texture object for each picture), then render all the movies. The next logical step is therefore to analyze how OpenGL renders a scene.</p>
<p><strong>From commands to images</strong></p>
<p>Without anything fancy added to it, OpenGL will display or draw primitives as they are submitted to it. In other words, OpenGL&#8217;s <em>display order</em> is OpenGL&#8217;s <em>render order</em>. However, to minimize state changes, it is often necessary to change the render order to group primitives that share some amount of state information. For example, if you have a red quad below a green quad below a second red quad, you&#8217;d normally render the first red quad first, then the green quad and finally the second red quad. However, it would be more efficient if you could render both red quads together, then change the front color and render the green quad. Unfortunately, if you do that, you&#8217;ll mess up your scene. OpenGL&#8217;s solution to this problem is called <em>the depth buffer</em>.</p>
<p><strong>Give me depth</strong></p>
<p>The depth buffer is essentially a two dimensional grayscale texture the same size as the OpenGL <em>viewport</em> (the area of the screen or window OpenGL is rendering into), which means there is one pixel in the depth buffer for every pixel in the viewport. Normally, a value of 0 means <em>closest to the viewer</em> and a value of 1 means <em>as far away from the viewer as possible</em>. Usually, the depth buffer is cleared with 1 at the beginning of every frame, in the same fashion as the <em>color buffer</em>.</p>
<p>With the depth buffer made writable and <em>depth testing</em> enabled, you can start specify a <em>depth coordinate</em> (also known as a <em>z coordinate</em>) for your primitives. That depth coordinate, once it is properly modified by applicable transform matrices and extrapolated for every <em>fragment</em> generated by <em>rasterizing</em> a given primitive, will be compared to the corresponding value already found in the depth buffer. If the new depth value passes the depth test, which usually means if it is <em>closer to the viewer than the previous value</em>, the fragment continues to be processed. Otherwise, the fragment is altogether dismissed, with the final effect of preserving whatever was already there in the final image that will be displayed on your screen.</p>
<p>In short, depth testing allows us to control display order while optimizing render order! It seems we have found our solution, but unfortunately things are not quite so straightforward. As will be made clear in an upcoming entry, depth testing has problems of its own. Stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2005/11/30/where-is-my-playable-riven-x/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Report-an-Apple-Bug Friday for August 26, 2005</title>
		<link>http://www.devklog.net/2005/08/26/report-an-apple-bug-friday-for-august-26-2005/</link>
		<comments>http://www.devklog.net/2005/08/26/report-an-apple-bug-friday-for-august-26-2005/#comments</comments>
		<pubDate>Sat, 27 Aug 2005 02:56:08 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.devklog.net/?p=20</guid>
		<description><![CDATA[It's time for Report-an-Apple-Bug Friday, even though it's not going to be about QTKit. Indeed, the bug I was reporting earlier on is far more complex than I anticipated. Read the full post for more details and for my weekly bug!]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s the favorite day of the week for Apple developers worldwide!</p>
<p>We have reports from <a href="http://inessential.com/2005/08/26.php" title="inessential" onclick="javascript:pageTracker._trackPageview ('/outbound/inessential.com');">Brent Simmons</a> and renowned <a href="http://www.drunkenblog.com/drunkenblog-archives/000635.html" title="DrunkenBlog" onclick="javascript:pageTracker._trackPageview ('/outbound/www.drunkenblog.com');">drunkenbatman</a> (and he&#8217;s got a pretty good one too). More bugs from <a href="http://www.chriswoods.com/archives/2005/08/report_a_bug_to.html" title="Chris Woods" onclick="javascript:pageTracker._trackPageview ('/outbound/www.chriswoods.com');">Chris Woods</a>, <a href="http://mikezornek.com/archives/2005/08/26/mac_sync_eats_homepages.php" title="Mike Zornek" onclick="javascript:pageTracker._trackPageview ('/outbound/mikezornek.com');">Mike Zornek</a>. I&#8217;ll try to add more links to this entry as bugs are posted, but it&#8217;s a bit hard to track them at the moment (someone should make a website to track those).</p>
<p>In any case, I was supposed to write about a QTKit bug or limitation having to do with opening movies embedded inside files at a particular known offset. After several hours of work, I can say with relative confidence that QuickTime is one awesomely weird piece of technology. For reasons that are beyond my understanding, trying to open a perfectly valid movie file using a data fork reference number and <a href="http://developer.apple.com/documentation/QuickTime/APIREF/newmoviefromproperties.htm" title="DrunkenBlog" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">NewMovieFromProperties</a> fails with a movie not found error (-2048). Creating a movie for the same file using a POSIX path or a CFURL works perfectly fine. Of course, since QTKit doesn&#8217;t expose functionality equivalent to <a href="http://developer.apple.com/documentation/QuickTime/APIREF/NewMoviePropertyCodes.htm" title="kQTDataLocationPropertyID_DataFork" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">kQTDataLocationPropertyID_DataFork</a>, there is no such equivalent problem in QTKit.</p>
<p>Which brings me to the code I posted in <a href="http://www.devklog.net/2005/08/21/on-report-an-apple-bug-fridays/" title="On Report-an-Apple-Bug Fridays">On Report-an-Apple-Bug Fridays</a> and to my current state of confusion. The snippet I posted is part of MHKKit, a Foundation-based framework I am currently developing as part of the Riven X project, a Mac OS X native re-implementation of the popular game by <a href="http://www.cyanworlds.com" title="Cyan Worlds" onclick="javascript:pageTracker._trackPageview ('/outbound/www.cyanworlds.com');">Cyan Worlds</a>. Riven data files contain several embedded QuickTime movies, and the only API that properly opens them is <a href="http://developer.apple.com/documentation/QuickTime/APIREF/newmoviefromproperties.htm" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">NewMovieFromProperties</a> with <a href="http://developer.apple.com/documentation/QuickTime/APIREF/NewMoviePropertyCodes.htm" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">kQTDataLocationPropertyID_DataFork</a> and <a href="http://developer.apple.com/documentation/QuickTime/APIREF/NewMoviePropertyCodes.htm" title="kQTMovieResourceLocatorPropertyID_FileOffset" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">kQTMovieResourceLocatorPropertyID_FileOffset</a> (which, incidentally, only works with <a href="http://developer.apple.com/documentation/QuickTime/APIREF/NewMoviePropertyCodes.htm" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">kQTDataLocationPropertyID_DataFork</a>). Pretty cute, isn&#8217;t it? But I get the message Apple, I won&#8217;t try to see logic in QuickTime anymore. I promise.</p>
<p><strong>I still have a bug to post</strong></p>
<p>But I still have a bug to post, although I&#8217;m almost certain it will finish as a duplicate because it&#8217;s such an obvious one. It involves the Dock and <a href="http://developer.apple.com/documentation/Carbon/Conceptual/understanding_utis/index.html" title="Uniform Type Identifiers" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">Uniform Type Identifiers</a>. The theory is that UTIs are as of Tiger the primary way to determine the type of a items on the system, be them files, filesystem objects or more abstract entities like contacts or calendar events. Everything else (OSTypes, extensions and MIME types) maps back to a UTI. Therefore, if one is write a Tiger-only application, one should normally use UTIs for purposes of file type identification. If you&#8217;re not familiar with UTIs, I encourage you to read the link above which explains in good details what they are about. The executive summary is that they are a most worthy successor to every other type identification mechanism we&#8217;ve had so far.</p>
<p>One area in particular where UTIs should be used is in the Info.plist file that can be found in every bundle when it&#8217;s time to declare the types of documents or entities a particular application supports. This is the well-known <a href="http://developer.apple.com/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/PListKeys.html" title="CFBundleDocumentTypes" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">CFBundleDocumentTypes</a> key in plists. Instead of using <em>CFBundleTypeExtensions</em> or <em>CFBundleTypeOSTypes</em>, one should use <em>LSItemContentTypes</em>, which is specified as an array of UTIs corresponding to the document type entry it belongs to(CFBundleDocumentTypes is an array itself). Unfortunately, this isn&#8217;t quite usable yet because the Dock doesn&#8217;t perform conformance tests properly.</p>
<p>If you read the documentation about UTIs, you know they are organized in hierarchies. This implies some form of relationships between UTIs, which is called <em>conformance</em>. A UTI <em>conforms</em> to another UTI if it expresses a refinement of the <em>conformed to type</em>. A very simple example of conformance is with JPEG images. It&#8217;s readily apparent that a JPEG image is generally speaking an image, and thus the <em>public.jpeg</em> UTI <em>conforms to</em> the <em>public.image</em> UTI.</p>
<p>Conformance is really important and amazing because for example an image editing application doesn&#8217;t need to declare a document type for every known image format – all it needs to do is declare one document type with the UTI <em>public.image</em> and suddenly the system knows that particular application can open <em>any image file</em>.</p>
<p>We call a <em>conformance test</em> the process of computing whether a particular UTI <em>conforms to</em> another given UTI. For instance, a conformance test of <em>public.jpeg</em> and <em>public.image</em> would indicate <em>public.jpegconforms to public.image</em>. Similarly, a conformance test of <em>public.mp3</em> and <em>public.image</em> would fail.</p>
<p>You can now most likely understand the bug when I say that the Dock doesn&#8217;t perform conformance tests properly. What happens is that if your application declares a document type with a UTI, say <em>public.jpeg</em>, the Dock will accept a file drop on that application&#8217;s icon only if the UTI of the file being dropped is <em>exactly public.jpeg</em>. If your document type uses <em>public.image</em>, the Dock will refuse the file drop. On the other hand, this works perfectly fine in the Finder.</p>
<p>Radar bug #<a href="rdar://4234661" title="Radar 4234661" onclick="javascript:pageTracker._trackPageview ('/outbound/4234661');">4234661</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2005/08/26/report-an-apple-bug-friday-for-august-26-2005/feed/</wfw:commentRss>
		</item>
		<item>
		<title>On Report-an-Apple-Bug Fridays</title>
		<link>http://www.devklog.net/2005/08/21/on-report-an-apple-bug-fridays/</link>
		<comments>http://www.devklog.net/2005/08/21/on-report-an-apple-bug-fridays/#comments</comments>
		<pubDate>Mon, 22 Aug 2005 02:58:07 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.devklog.net/?p=15</guid>
		<description><![CDATA[About Report-an-Apple-Bug Fridays - it's a good thing!]]></description>
			<content:encoded><![CDATA[<p><a href="http://inessential.com/2005/08/19.php" onclick="javascript:pageTracker._trackPageview ('/outbound/inessential.com');">Brent Simmons writes on inessential about Report-an-Apple-Bug Friday</a>:</p>
<blockquote><p>If you reported a bug to Apple today—it being Report-an-Apple-Bug Friday—please leave a comment and say what your bug id is. (Oh, and why not say what the bug is too. But we gotta have an id—a bug id is a badge of honor.)</p></blockquote>
<p>I think weekly bug reports are a pretty good idea and so I will try to follow this new tradition among Mac developers as dutifully as I can. My first one will probably be about either a limitation or a bug in <a href="http://developer.apple.com/documentation/QuickTime/Conceptual/QTKitProgrammingGuide/index.html" title="QTKit Programming Guide" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">QTKit</a> related to movie creation.</p>
<p>The problem has to do with opening embedded QuickTime movies – that is, opening movies that are somewhere inside another file. This is supported by QuickTime through various means:</p>
<ul>
<li>You can either open the movie using an existing fork, or</li>
<li>you can open the movie using a file reference of some sort</li>
</ul>
<p>In both cases, you specify the offset of the QuickTime movie when the movie is created, which in our case we assume is a known correct value. The best way to open a movie in such a way with QuickTime 7 is by using the new <a href="http://developer.apple.com/documentation/QuickTime/APIREF/newmoviefromproperties.htm" title="NewMovieFromProperties" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">NewMovieFromProperties</a> function. The following code demonstrate how this can be done. It&#8217;s an excerpt from MHKKit, a framework project I will soon introduce in more details.</p>
<p><code>// setup the movie properties<br />
	Boolean	active = TRUE;<br />
	QTVisualContextRef visualContext = NULL;<br />
	QTNewMoviePropertyElement newMovieProperties[] = {<br />
		{kQTPropertyClass_DataLocation, kQTDataLocationPropertyID_DataFork, sizeof(forkRef), &#038;forkRef, 0},<br />
		{kQTPropertyClass_MovieResourceLocator, kQTMovieResourceLocatorPropertyID_FileOffset, sizeof(qt_offset), &#038;qt_offset, 0},<br />
		{kQTPropertyClass_Context, kQTContextPropertyID_VisualContext, sizeof(visualContext), &#038;visualContext, 0},<br />
		{kQTPropertyClass_NewMovieProperty, kQTNewMoviePropertyID_Active, sizeof(active), &#038;active, 0}<br />
	};</p>
<p>	// make the movie<br />
	Movie aMovie = NULL;<br />
	err = NewMovieFromProperties(sizeof(newMovieProperties) / sizeof(newMovieProperties[0]), newMovieProperties, 0, NULL, &#038;aMovie);</code></p>
<p>In this particular case, I use an existing fork to open the movie, but using a URL or FSRef to the file from which that fork was opened shouldn&#8217;t make any difference in theory. That is unfortunately where <a href="http://developer.apple.com/documentation/QuickTime/Reference/QTCocoaObjCKit/Classes/QTMovie.html" title="QTMovie" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">QTMovie</a> seems to be broken. Attempting to create an instance of that class with <a href="http://developer.apple.com/documentation/QuickTime/Reference/QTCocoaObjCKit/Classes/QTMovie.html#//apple_ref/doc/uid/20002391-CJBFFAGC" title="- initWithAttributes:error:" class="API" onclick="javascript:pageTracker._trackPageview ('/outbound/developer.apple.com');">- initWithAttributes:error:</a> and the <em>QTMovieFileOffsetAttribute</em> attribute fails, even if the specified file and offset are the same.</p>
<p>I haven&#8217;t done any digging in order to figure out exactly what is broken, but I intent to before posting my bug report. Remember kids: detailed bug reports make QA people happy and bug squishing a joy.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2005/08/21/on-report-an-apple-bug-fridays/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Backing up Subversion daily (updated)</title>
		<link>http://www.devklog.net/2005/06/03/backing-up-subversion-daily-updated/</link>
		<comments>http://www.devklog.net/2005/06/03/backing-up-subversion-daily-updated/#comments</comments>
		<pubDate>Fri, 03 Jun 2005 15:05:33 +0000</pubDate>
		<dc:creator>Jean-François Roy</dc:creator>
		
		<category><![CDATA[Shell script]]></category>

		<guid isPermaLink="false">https://zohar.dyndns.org/?p=7</guid>
		<description><![CDATA[A simple shell script to backup Subversion repositories to a remote server.]]></description>
			<content:encoded><![CDATA[<p>I run a small Subversion server for my personal software projects and other things one would want to put under version control. Never knowing when the ugly sound of a dying hard drive will sneak up on me, I needed some kind of script to do daily backups of the repositories. After checking my options, I decided I would be doing hotcopies of the repositories piped to tar with bz2 compression and send the resulting archives over to another machine using scp, handling password-less authentication with an SSH key pair.</p>
<p>Since I&#8217;m insane (more than most, in any case), I wrote a bash shell script. Here it is:</p>
<p><code>#!/bin/bash<br />
##<br />
##  svn-backup.sh -- Daily backup script for subversion repositories<br />
##  Copyright (c)  2005, Jean-Francois Roy<br />
##</p>
<p>## CONFIGURATION</p>
<p># local path to the repositories<br />
svnroot="/usr/local/svn"</p>
<p># install path of subversion (where svnadmin and svnlook may be found)<br />
svninstallroot="/opt/local/bin"</p>
<p># remote host for scp, including any required user and password<br />
remotehost="bahamut@zohar.local"</p>
<p># path on remote host where the repository archives will be uploaded<br />
remotedir="Documents/Backups/subversion/`date +%d-%m-%G`"</p>
<p>## CODE</p>
<p># check parameters<br />
if [ -z ${svnroot} ]  || [ ! -d ${svnroot} ]<br />
then<br />
    echo &#8220;FATAL: invalid svnroot variable&#8221;<br />
    exit<br />
fi</p>
<p>if [ -z ${svninstallroot} ] || [ ! -d ${svninstallroot} ]; then<br />
    echo &#8220;FATAL: invalid svninstallroot variable&#8221;<br />
    exit<br />
fi</p>
<p>if [ ! -x ${svninstallroot}/svnlook ] || [ ! -x ${svninstallroot}/svnadmin ]; then<br />
    echo &#8220;FATAL: could not find Subversion in provided install path&#8221;<br />
    exit<br />
fi</p>
<p>if [ -z ${remotehost} ]; then<br />
    echo &#8220;FATAL: invalid remotehost variable&#8221;<br />
    exit<br />
fi</p>
<p>if [ -z ${remotedir} ]; then<br />
    echo &#8220;FATAL: invalid remotedir variable&#8221;<br />
    exit<br />
fi</p>
<p># announce that we&#8217;re running<br />
echo &#8220;Running svn-backup.sh at `date`&#8221;</p>
<p># perform a mkdir on the remote host<br />
ssh ${remotehost} &#8220;mkdir -p ${remotedir}&#8221;</p>
<p># change into the current dir<br />
cd ${svnroot} </p>
<p>for filename in *<br />
do<br />
    # get the current revision<br />
    revision=`${svninstallroot}/svnlook youngest &#8220;${filename}&#8221;`</p>
<p>    # keep the user happy<br />
    echo &#8220;Backing up ${filename} r${revision}&#8221;</p>
<p>    # make sure the repo is OK<br />
    echo &#8221;    &#8212;>  Recovering the repository&#8221;<br />
    ${svninstallroot}/svnadmin recover &#8211;wait &#8220;${filename}&#8221; > /dev/null</p>
<p>    # did the recover operation fail?<br />
    if [ $? -ne 0 ]; then<br />
        echo &#8221;    Backup of ${filename} r${revision} failed because recovery failed!&#8221;<br />
        break<br />
    fi</p>
<p>    # create the backup folder<br />
    base=&#8221;$RANDOM&#8221;<br />
    if [ -e "/tmp/${base}" ]<br />
    then<br />
        rm -Rf &#8220;/tmp/${base}&#8221;<br />
    fi<br />
    mkdir &#8220;/tmp/${base}&#8221;</p>
<p>    # hotcopy<br />
    echo &#8221;    &#8212;>  Hotcopying the repository&#8221;<br />
    ${svninstallroot}/svnadmin hotcopy &#8211;clean-logs &#8220;${filename}&#8221; &#8220;/tmp/${base}/${filename}&#8221;</p>
<p>    # did the hotcopy fail?<br />
    if [ $? -ne 0 ]; then<br />
        echo &#8220;Backup of ${filename} r${revision} failed because hotcopy failed!&#8221;<br />
        rm -Rf &#8220;/tmp/${base}&#8221;<br />
        break<br />
    fi</p>
<p>    # compress the hotcopy<br />
    echo &#8221;    &#8212;>  Packaging the repository in a tar.bz2 archive&#8221;<br />
    archive=&#8221;${filename}-r${revision}.tar.bz2&#8243;<br />
    tar -cjpf &#8220;/tmp/${base}/${archive}&#8221; -C &#8220;/tmp/${base}&#8221; &#8220;${filename}&#8221;</p>
<p>    # send it over<br />
    echo &#8221;    &#8212;>  Copying repository archive to remote host&#8221;<br />
    scp -B -q &#8220;/tmp/${base}/${archive}&#8221; &#8220;${remotehost}:${remotedir}/${archive}&#8221;</p>
<p>    # done with the backup folder<br />
    echo &#8221;    &#8212;>  Cleaning up&#8221;<br />
    rm -Rf &#8220;/tmp/${base}&#8221;<br />
done</p>
<p>echo &#8220;svn-backup.sh completed at `date`&#8221;<br />
echo &#8220;&#8221;</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.devklog.net/2005/06/03/backing-up-subversion-daily-updated/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.797 seconds -->
