<?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="2.0">  <channel>
    <title>Cyan Canyon</title>
    <link>http://www.cyancanyon.com</link>
    <description>Cyan Canyon News, Updates, Bias</description>
    <language>en-us</language>
    <lastBuildDate>Sun, 15 Mar 2009 00:00:00</lastBuildDate>
	  <item>
      <title>A Different Way to GetFiles</title>
      <link>http://www.cyancanyon.com/news/?id=17</link>
      <description><![CDATA[<p>There comes a time in the life of every man, woman and cylon that they must retrieve a list of the files and/or subdirectories from a folder using .NET. Most of us use the accessible static methods from the Directory class...</p><code>string[] files = Directory.GetFiles(@"C:\path");<br>string[] subdirs = Directory.GetDirectories(@"C:\path");</code><p>These are all fine and good for everyone... until you want to filter by file attributes or filter by a regular expression or filter by file size or filter by file date or get files and subdirectories at the same time or iterate through the results instead of just returning the whole lot or doing any of these things quickly. Then you need something else.</p><p>The WIN32 API provides a much more versatile way of getting at the contents of a directory and it can be accessed pretty easily using p/invoke. It exists in kernel32.dll on Windows and coredll.dll on Windows Mobile. I'm going to demonstrate the Windows Mobile specifics because that is how I have used them and there are many googleable examples of the Windows desktop version.</p><p>There are 3 functions we'll need...</p><code>using System.Runtime.InteropServices;<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa914391.aspx">http://msdn.microsoft.com/en-us/library/aa914391.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx</a></span><br>[DllImport("coredll.dll", CharSet = CharSet.Auto)]<br>private static extern IntPtr FindFirstFile(String lpFileName, ref WIN32_FIND_DATA lpFindFileData);<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa914380.aspx">http://msdn.microsoft.com/en-us/library/aa914380.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa364428(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa364428(VS.85).aspx</a></span><br>[DllImport("coredll.dll", CharSet = CharSet.Auto)]<br>private static extern bool FindNextFile(IntPtr hFindFile, ref WIN32_FIND_DATA lpFindFileData);<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa911929.aspx">http://msdn.microsoft.com/en-us/library/aa911929.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa364413(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa364413(VS.85).aspx</a></span><br>[DllImport("coredll.dll", CharSet = CharSet.Auto)]<br>private static extern bool FindClose(IntPtr hFindFile);</code><p>The basics here are that you call FindFirstFile with the path and you get back a handle and data for the first file or directory. You then call FindNextFile with the handle to get each subsequent file or directory until they are all gone or you are done. Then you wrap it up nicely by calling FindClose to clean up.</p><p>The FindFirstFile function expects its path parameter to end in '\*' as in 'C:\path\*'. If you leave off the '*', you will get an invalid handle and no data. If you leave off the '\*' altogether as in 'C:\path', you will get data for the 'path' directory and nothing else; no files, no subdirectories.</p><p>We'll need a few constants and a couple data structures...</p><code>private const int MAX_PATH = 260; <span class="comment">//maximum length of a file name</span><br>private const int INVALID_HANDLE_VALUE = -1;<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa915351.aspx">http://msdn.microsoft.com/en-us/library/aa915351.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx">http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx</a></span><br>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]<br>private struct FILETIME {<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwLowDateTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwHighDateTime;<br>}<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa914427.aspx">http://msdn.microsoft.com/en-us/library/aa914427.aspx</a></span><br>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]<br>private struct WIN32_FIND_DATA {<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwFileAttributes;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftCreationTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastAccessTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastWriteTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeHigh;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeLow;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwOID;<br>&nbsp;&nbsp;&nbsp;&nbsp;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]<br>&nbsp;&nbsp;&nbsp;&nbsp;public string cFileName;<br>}</code><p>It is important to note that the layout for WIN32_FIND_DATA is slightly different in Windows. If you are using the wrong definition in your Windows Mobile application, you'll be able to tell because the first two characters of all your file names will be missing.</p> <p>Here is the Windows version...</p><code>private const int MAX_ALTERNATE = 14;<br><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx</a></span><br>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]<br>private struct WIN32_FIND_DATA {<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwFileAttributes;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftCreationTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastAccessTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastWriteTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeHigh;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeLow;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwReserved0;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwReserved1;<br>&nbsp;&nbsp;&nbsp;&nbsp;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]<br>&nbsp;&nbsp;&nbsp;&nbsp;public string cFileName;<br>&nbsp;&nbsp;&nbsp;&nbsp;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]<br>&nbsp;&nbsp;&nbsp;&nbsp;public string cAlternateFileName;<br>}</code><p>The filename you get out does not include the path. FileSizeLow is the file size you want in most cases. If FileSizeHigh has something in it, then your file is huge (4GB+) and you need to combine it with FileSizeLow to make an 8-byte integer. Everything else should be pretty self-explanatory or easy to interpret from MSDN.</p><p>Now then, the function to actually get the files is pretty simple, but varies greatly depending on what you want out of it. We don't want to have to remember to add the '\*' to the path every time we call this function so we'll take care of all that here.</p><code><span class="comment">//function signature will vary with the body.</span><br>public static void GetFiles(string path) {<br>&nbsp;&nbsp;&nbsp;&nbsp;IntPtr handle;<br>&nbsp;&nbsp;&nbsp;&nbsp;WIN32_FIND_DATA filedata;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//we want path to have something useful that we can add to the filename</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//in case we want the entire path, so lets drop the final '*' if it exists</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//and add it later when we need it.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;if (path.EndsWith("*")) path = path.Substring(0, path.Length - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//make sure the path ends with the backslash though.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;if (!path.EndsWith(@"\")) path += @"\";<br><br>&nbsp;&nbsp;&nbsp;&nbsp;filedata = new WIN32_FIND_DATA();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//get the first file, get the rest of the files, and close.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;handle = FindFirstFile(path + "*", ref filedata);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (handle.ToInt32() != INVALID_HANDLE_VALUE) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (filedata.cFileName != "." &amp;&amp; filedata.cFileName != "..") {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//filtering and storage logic here</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} while (FindNextFile(handle, ref filedata));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} finally {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FindClose(handle);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</code><p>All your filtering just fits inside. The most basic filter, checking for a directory would be something like this...</p><code>List&lt;string&gt; files = new List&lt;string&gt;();<br>uint directoryattribute = (uint)FileAttributes.Directory; <span class="comment">//16 or Hx10</span><br><br>...<br><br>if ((filedata.dwFileAttributes &amp; directoryattribute) != directoryattribute) {<br>&nbsp;&nbsp;&nbsp;&nbsp;files.Add(path + filedata.cFileName);<br>}</code><p>Combine this with a filter based on a regular expression passed in to the function...</p><code>Regex regex = new Regex(@"\.txt");<br><br>...<br><br>if ((filedata.dwFileAttributes &amp; directoryattribute) != directoryattribute) {<br>&nbsp;&nbsp;&nbsp;&nbsp;if (regex == null || regex.Match(filedata.cFileName).Success) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files.Add(path + filedata.cFileName);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</code><p>If you are going for speed on multiple calls, do your best not to create any class instances in your function. Pass in your regex and result container.</p><p>Here is the final function I ended up using...</p><code>public static void GetFiles(string path, List&lt;string&gt; files, Regex fileregex, List&lt;string&gt; subdirs, Regex subdirregex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;IntPtr handle;<br>&nbsp;&nbsp;&nbsp;&nbsp;WIN32_FIND_DATA filedata;<br>&nbsp;&nbsp;&nbsp;&nbsp;uint dirattribute = (uint)FileAttributes.Directory;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;if (path.EndsWith("*")) path = path.Substring(0, path.Length - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (!path.EndsWith(@"\")) path += @"\";<br><br>&nbsp;&nbsp;&nbsp;&nbsp;filedata = new WIN32_FIND_DATA();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;handle = FindFirstFile(path + "*", ref filedata);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (handle.ToInt32() != -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (filedata.cFileName != "." &amp;&amp; filedata.cFileName != "..") {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((filedata.dwFileAttributes &amp; dirattribute) == dirattribute) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (subdirs != null &amp;&amp; (subdirregex == null || subdirregex.Match(filedata.cFileName).Success)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subdirs.Add(path + filedata.cFileName);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (files != null &amp;&amp; (fileregex == null || fileregex.Match(filedata.cFileName).Success)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files.Add(path + filedata.cFileName);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} while (FindNextFile(handle, ref filedata));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} finally {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FindClose(handle);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</code><p>...called from something like this to get all images in a directory tree...</p><code>public static List&lt;string&gt; FindImages(string path) {<br>&nbsp;&nbsp;&nbsp;&nbsp;List&lt;string&gt; files, subdirs;<br>&nbsp;&nbsp;&nbsp;&nbsp;Regex regex;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;files = new List&lt;string&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;subdirs = new List&lt;string&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;regex = new Regex(@"\.(png|jpg|jpeg|gif)$");<br><br>&nbsp;&nbsp;&nbsp;&nbsp;subdirs.Add(path);<br>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; subdirs.Count; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//since subdirectories may be added to the list with each iteration,</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//we end up searching the entire tree level by level</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetFiles(subdirs[i], files, regex, subdirs, null); <span class="comment">//no filter on the subdirectories</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;return files;<br>}</code><p>Finally, if you don't want to wait for the entire list to be returned before you start processing, you can use the yield statement to create a file iterator... but I'm not going to tell you how to do that. There is a great article over on <a href="http://www.codeproject.com/KB/files/FileSystemEnumerator.aspx">codeproject.com</a> that outlines this. They use a SafeHandle that is not available on Windows Mobile, but the basic idea is the same.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=17</guid>
      <pubDate>Sun, 15 Mar 2009 00:00:00</pubDate>
    </item>
	  <item>
      <title>AAAA for iPod</title>
      <link>http://www.cyancanyon.com/news/?id=16</link>
      <description><![CDATA[<p>I just spent the last couple hours in a skirmish with my 160GB classic and iTunes 8 on my mac. I'm not one of those people who obsessively searches for the happy in every pile of shit, but I do feel like I learned something useful on this one.</p><p>If you've ever scoured the file structure on your iPod then you'll know that there is a hidden directory called iPod_Control where iTunes places all your media files. These files are split up into a series of F## folders and given ambiguous names like JFUD.mp3.</p><p>Well my little debacle tonight led me to the conclusion that files specifically named AAAA.*, AAAA 1.*, AAAA 2.* etc. have significance. That is, they are wasting your precious drive space. Now I haven't tested this theory on any other iPod or in any other situation, but here's how it went...</p><p>iTunes 8 was causing me trouble in a variety of ways. One was a computer authorization issue that I don't think had anything to do with this tale. The other issue was syncing problems with my iPod. I store lots of movies and TV shows on my iPod and tonight I wanted to copy over <span style="font-style: italic">Apocalypto</span> and <span style="font-style: italic">Primer</span>. To my dismay I was out of space, something had to go. Oh look, the girlfriend's boring chick movies that she wanted me to put on there and then she never watched them. DELETE. OK, space acquired... Copy up my mp4 files, looks good... Eject the iPod, make sure they show up in the movies list... Damn. They aren't there. Hooked it back up and checked the movie list in iTunes, now the three chick movies are back and no Mel Gibson or brilliant indies. WTF? Try again, delete the movies, this time just upload <span style="font-style: italic">Primer</span> since its a much smaller file... Same thing. So after some time of alternating between futile googling, unhelpful Apple support pages and the rebooting of all involved devices and software I got the thing to start working right again. Or so I thought.</p><p>I copied up <span style="font-style: italic">Primer</span> and everything went just fine. Girly movies gone and entrepreneurial time-travel present and accounted for. Then I tried to sync my podcasts since I'm a bit behind on <span style="font-style: italic">The Onion</span> and I hit a new roadblock: Disk Is Full. My "Other" file types were taking up a full 5.5 GB of disk space. I've had these mystery files (and by mystery I mean album art and crap like that) take a few hundred MB before but this is just stupid. I'm sure its those blasted chick movies that just won't give up and DIE! So I open up Omni DiskSweeper and scan away. Nothings hiding in the trash or anywhere else, its all in the F## folders. I'm guessing that iTunes just removed the references to the video files and couldn't delete them, so I went through checking everything over 1GB. It turned out that there was not one, but three copies of <span style="font-style: italic">Primer</span> on the iPod, though it was only listed once. This is where I noticed that although the three files were in three different directories, one was named some combination of random letters but the other two were both called AAAA.mp4. Suspicious, I went looking for more AAAA files, and found many. There was an AAAA.mp4 that was the lost <span style="font-style: italic">Apocalypto</span> file, a <span style="font-style: italic">BSG</span> episode that I had tried to upload during the whole thing, as well as a ton of Doyle Redland reporting from my many unsuccessful attempts.</p><p>Realizing that this was an iPod restore or not-restore situation and being that I couldn't specifically remember ever being wrong, I deleted all files in the F## folders that began with AAAA. I am now happy to report that my iPod is back to its normal level of "Other" file space, devoid of chick flicks, brimming with the talent of Carruth and teeming with questionable Cylon mind games.</p><p>Me: 1, iPod: 0</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=16</guid>
      <pubDate>Tue, 16 Sep 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>FooBrain v1.0 Released</title>
      <link>http://www.cyancanyon.com/news/?id=15</link>
      <description><![CDATA[<p><a href="http://www.cyancanyon.com/software/foobrain/">Cyan Canyon FooBrain</a> version 1.0 for Windows Mobile Pocket PCs has been released for download. FooBrain is a developers tool that tracks applications/websites and their platforms, versions, dependencies, changes, ancillary files and general notes in a simple hierarchal structure. It also provides an easy method for recording which changes and updates have or will be released in which version.</p><ul><li>Organize your applications and websites with both preset and user-defined tags.</li><li>Track your version history, current releases and planned updates for each platform of an application.</li><li>Record bugs and planned updates for later reference as you develop your program.</li><li>Keep a history of changes for each version and platform.</li><li>Track dependencies and see which applications are referencing any particular program or library.</li><li>Keep tabs on third-party software that your applications use.</li><li>Record information about installers, patches and other auxiliary programs.</li><li>Keep general notes for each version.</li></ul><div style="text-align: center"><img src="http://www.cyancanyon.com/news/images/foobrainsplash.png" alt="FooBrain Splash Screen" title="FooBrain Splash Screen" /><span>&nbsp;&nbsp;&nbsp;</span><img src="http://www.cyancanyon.com/news/images/scr_apps.png" alt="FooBrain Application List" title="FooBrain Application List" /></div><p>Tell your friends! <a href="http://www.cyancanyon.com/software/foobrain/">Download it here!</a></p><!--where-->]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=15</guid>
      <pubDate>Sat, 30 Aug 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>FooBrain Help Files</title>
      <link>http://www.cyancanyon.com/news/?id=14</link>
      <description><![CDATA[<p>The next big thing on Cyan Canyon is <a href="http://www.cyancanyon.com/software/foobrain/">FooBrain</a>, the application planner/version tracker/bug tracker/dependency tracker for pocket PCs. I've been using variations of this program (I used to call it AppTrack) for the past three years or so. There are way too many ideas in my head and not enough time to deal with them all, but I keep track of them anyway as various programs fade in and out of my interest. I also use it to keep tabs on my projects at work so right now I think I have nearly 100 applications, maybe 300 versions and I'd guess upwards of 2500 change notes that need to be organized. FooBrain handles it perfectly (of course, if it didn't I would fix it, now wouldn't I?).</p><p>It hasn't been released yet but I did upload the <a href="http://www.cyancanyon.com/software/foobrain/">help files</a>. Not that they would mean much without the application or even screenshots yet (thats coming next) but I figured someone out there might want a peek.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=14</guid>
      <pubDate>Sat, 02 Aug 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>Skipping the Mac Trash</title>
      <link>http://www.cyancanyon.com/news/?id=13</link>
      <description><![CDATA[<p>As much as I think that Mac OS X is a generally superior operating system, there is a thing or two that I gripe about. One is the Windows luxury of being able to yank out my USB key at any random time and without any prerequisite action, but I'm not going to talk about that one today. Another is the lack of a skip-the-trash delete command similar to shift-delete in Windows.</p><p>I know its easy to send something to the trash and then empty the trash and people have even automated this, but its not what I'm looking for. I want to completely delete a file without also deleting everything that just happened to be in the trash too. I also want to do it with a keyboard shortcut, but I haven't gotten there yet.</p><p>Here is my dock...</p><img alt="dock with toilet next to trash" title="toilet icon" src="http://www.cyancanyon.com/news/images/docktoilet.png"><p>Oh yeah, theres a toilet on my dock. Drag a file to the trash and it gets marked for deletion. Drag it to the toilet, and you get...</p><img alt="delete notification" title="delete notification" src="http://www.cyancanyon.com/news/images/toiletconfirm.png"><p>This is done with some AppleScript which calls some shell script together with configurable folder actions.</p><p>NOTE: This is all done on OS X Tiger, please let me know if there are caveats for (Snow)?Leopard.</p><p>The UI is just a folder where the icon has been set to the toilet pic. Then it is dragged down into the dock for easy access.</p><p>Next there is the AppleScript. This routine gets the list of files in a directory. For each file, it checks to make sure its not the .DS_Store or the icon file and if not, calls the shell command to delete it.</p><code><span>on adding folder items to this_folder after receiving these_items</span><br>&nbsp;&nbsp;<span>set the item_count to the count of these_items</span><br><br>&nbsp;&nbsp;<span class="comment">--confirm the deletion</span><br>&nbsp;&nbsp;<span>set the_choice to display dialog (item_count as string) &amp; " items are set to be deleted." buttons {"Delete", "Move to Trash"} default button 1</span><br>&nbsp;&nbsp;<span>set the_result to button returned of the_choice</span><br>&nbsp;&nbsp;<span>if the the_result is equal to "Delete" then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>repeat with filename in these_items</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--don't process the .DS_Store or the icon file</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if ((filename as string) ends with ":.DS_Store") or ((filename as string) ends with ":Icon") or ((filename as string) ends with ":Icon ") then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--do nothing</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>try</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the delimiter is a colon, so we have to replace it with a slash so the shell command can read it</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set tid to text item delimiters</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to missing value</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to {":"}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set fnstring to text items of (filename as text)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to {"/"}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if last item of fnstring is equal to "" then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to "/" &amp; ((items 2 thru -2 of fnstring) as string)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to "/" &amp; ((items 2 thru -1 of fnstring) as string)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end if</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to tid</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>on error msg</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>display dialog msg</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end try</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--call the shell command</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the 'r' flag indicates that if the pathname points to a directory, it should be deleted as well as all files and folders it contains</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the 'f' flag indicates that the deletion is forced, nonexistent files are ignored and there are no prompts</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>do shell script "rm -rf \"" &amp; new_filename &amp; "\""</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end if</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>end repeat</span><br>&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--If you changed your mind, move it to the trash where it will be recoverable</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>tell application "Finder" to delete these_items</span><br>&nbsp;&nbsp;<span>end if</span><br><span>end adding folder items to</span></code><p>And then we just attach the script as a folder action to the toilet folder. Then, any time files are added to the folder (for example, by dragging them onto the dock icon) the script is fired off and the files deleted.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=13</guid>
      <pubDate>Tue, 10 Jun 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>Great Circle Distances</title>
      <link>http://www.cyancanyon.com/news/?id=12</link>
      <description><![CDATA[<p>Point to point distances are needed all throughout applications we've written, most notably, Smache. So getting accurate distance values was obviously very important. Unfortunately, the pythagorean theorem doesn't cut it when we're looking for distances across the surface of a planet, so a slightly more complicated function had to be used.</p><p>What we wanted was the great circle distance. Great circles are lines that encircle a sphere leaving equal-sized halves on either side. Any two points on a sphere can be connected with one and only one great circle, except for the cases where the points are exactly opposite each other or exactly the same in which infinite great circles can pass through the two points. To come up with the distance between two points along their respective great circle, we need a little trig and the <a href="http://en.wikipedia.org/wiki/Haversine_formula">haversine formula</a>.</p><p>NOTE: Smache was written in .NET with C# so my examples are also, but I included the javascript representation too because, well, javascript rocks.</p><p>The input parameters to the distance calculation function are the latitude and longitude for point A and the latitude and longitude for point B. We also needed a radius constant which was set at 6372 kilometers, widely agreed upon as the average radius of the earth. Note that because of the irregular shape of the earth, the resulting distance may be off by as much as half a percent.</p><code><span>const double EARTH_RADIUS = 6372;</span><br><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) { }</span></code><p>The haversine formula expects values to be in radians rather than degrees, so the first task is to make that conversion, simply dividing by 180 over pi.</p><code><span>double latARad = latA / (180 / Math.PI);</span><br><span>double longARad = longA / (180 / Math.PI);</span><br><span>double latBRad = latB / (180 / Math.PI);</span><br><span>double longBRad = longB / (180 / Math.PI);</span></code><p>The formula will also use delta values, so in the interest of comprehension we'll create those variables as well.</p><code><span>double latDelta = latBRad - latARad;</span><br><span>double longDelta = longBRad - longARad;</span><br></code><p>Next is where the haversine comes in. In order to eliminate duplicate equations I've split the formula into two statements.</p><code><span>double haversineA = Math.Pow(Math.Sin(latDelta / 2), 2) + Math.Cos(latARad) * Math.Cos(latBRad) * Math.Pow(Math.Sin(longDelta / 2), 2);</span><br><span>double haversineB = 2 * Math.Atan2(Math.Sqrt(haversineA), Math.Sqrt(1 - haversineA));</span><br></code><p>Finally we simply need to multiply by the radius to get the final distance.</p><code><span>double finalDistance = haversineB * EARTH_RADIUS;</span></code><p>So the function ends up like so...</p><code><span>const double EARTH_RADIUS = 6372;</span><br><br><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latARad = latA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longARad = longA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latBRad = latB / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longBRad = longB / (180 / Math.PI);</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latDelta = latBRad - latARad;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longDelta = longBRad - longARad;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double haversineA = Math.Pow(Math.Sin(latDelta / 2), 2) + Math.Cos(latARad) * Math.Cos(latBRad) * Math.Pow(Math.Sin(longDelta / 2), 2);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double haversineB = 2 * Math.Atan2(Math.Sqrt(haversineA), Math.Sqrt(1 - haversineA));</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double finalDistance = haversineB * EARTH_RADIUS;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return finalDistance;</span><br><span>}</span></code><p>or in javascript...</p><code><span>var EARTH_RADIUS = 6372;</span><br><br><span>function GreatCircleDistance(latA, longA, latB, longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latARad = latA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longARad = longA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latBRad = latB / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longBRad = longB / (180 / Math.PI);</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latDelta = latBRad - latARad;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longDelta = longBRad - longARad;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var haversineA = Math.pow(Math.sin(latDelta / 2), 2) + Math.cos(latARad) * Math.cos(latBRad) * Math.pow(Math.sin(longDelta / 2), 2);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var haversineB = 2 * Math.atan2(Math.sqrt(haversineA), Math.sqrt(1 - haversineA));</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var finalDistance = haversineB * EARTH_RADIUS;</span><br><br> <span>&nbsp;&nbsp;&nbsp;&nbsp;return finalDistance;</span><br><span>}</span></code><br><p>And if you're into one-liners...</p><code><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return 6372 * 2 * Math.Atan2(Math.Sqrt(Math.Pow(Math.Sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.Cos((latA / (180 / Math.PI))) * Math.Cos((latB / (180 / Math.PI))) * Math.Pow(Math.Sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2)), Math.Sqrt(1 - (Math.Pow(Math.Sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.Cos((latA / (180 / Math.PI))) * Math.Cos((latB / (180 / Math.PI))) * Math.Pow(Math.Sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2))));</span><br><span>}</span><br><br><span>function GreatCircleDistance(latA, longA, latB, longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return 6372 * 2 * Math.atan2(Math.sqrt(Math.pow(Math.sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.cos((latA / (180 / Math.PI))) * Math.cos((latB / (180 / Math.PI))) * Math.pow(Math.sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2)), Math.sqrt(1 - (Math.pow(Math.sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.cos((latA / (180 / Math.PI))) * Math.cos((latB / (180 / Math.PI))) * Math.pow(Math.sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2))));</span><br><span>}</span></code>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=12</guid>
      <pubDate>Sat, 07 Jun 2008 00:00:00</pubDate>
    </item>
  </channel>
</rss>
