<?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>Chris' Babble</title>
	<atom:link href="http://splinter.com.au/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://splinter.com.au/blog</link>
	<description>Syntax highlighting is from heaven, Perl is from hell...</description>
	<pubDate>Wed, 04 Aug 2010 09:09:34 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.5</generator>
	<language>en</language>
			<item>
		<title>How to implement DES and Triple DES from scratch</title>
		<link>http://splinter.com.au/blog/?p=269</link>
		<comments>http://splinter.com.au/blog/?p=269#comments</comments>
		<pubDate>Wed, 04 Aug 2010 09:09:34 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=269</guid>
		<description><![CDATA[A simple Ruby implementation with lots of comments. This document explains how to implement DES, from the perspective of learning how the algorithm works. Simplicity and learnability are favoured over optimisation unlike most open source crypto code, so hopefully this will make it easier for people to understand and de-mystify DES and block-cipher cryptography in [...]]]></description>
			<content:encoded><![CDATA[<p>A simple Ruby implementation with lots of comments. This document explains how to implement DES, from the perspective of learning how the algorithm works.<span id="more-269"></span> Simplicity and learnability are favoured over optimisation unlike most open source crypto code, so hopefully this will make it easier for people to understand and de-mystify DES and block-cipher cryptography in general.</p>
<p>Code can be found here: <a href="http://github.com/chrishulbert/crypto">http://github.com/chrishulbert/crypto</a></p>
<p><a title="View How to Implement DES in Ruby on Scribd" href="http://www.scribd.com/doc/33810102/How-to-Implement-DES-in-Ruby" style="margin: 12px auto 6px auto; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; -x-system-font: none; display: block; text-decoration: underline;">How to Implement DES in Ruby</a> <object id="doc_885754607844891" name="doc_885754607844891" height="500" width="100%" type="application/x-shockwave-flash" data="http://d1.scribdassets.com/ScribdViewer.swf" style="outline:none;" rel="media:document" resource="http://d1.scribdassets.com/ScribdViewer.swf?document_id=33810102&#038;access_key=key-2ktxsuac5s0bwsd01120&#038;page=1&#038;viewMode=list" xmlns:media="http://search.yahoo.com/searchmonkey/media/" xmlns:dc="http://purl.org/dc/terms/" ><param name="movie" value="http://d1.scribdassets.com/ScribdViewer.swf"><param name="wmode" value="opaque"><param name="bgcolor" value="#ffffff"><param name="allowFullScreen" value="true"><param name="allowScriptAccess" value="always"><param name="FlashVars" value="document_id=33810102&#038;access_key=key-2ktxsuac5s0bwsd01120&#038;page=1&#038;viewMode=list"><embed id="doc_885754607844891" name="doc_885754607844891" src="http://d1.scribdassets.com/ScribdViewer.swf?document_id=33810102&#038;access_key=key-2ktxsuac5s0bwsd01120&#038;page=1&#038;viewMode=list" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="500" width="100%" wmode="opaque" bgcolor="#ffffff"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=269</wfw:commentRss>
		</item>
		<item>
		<title>How to use Cookies in Struts 2 with ServletRequest and ServletResponse</title>
		<link>http://splinter.com.au/blog/?p=310</link>
		<comments>http://splinter.com.au/blog/?p=310#comments</comments>
		<pubDate>Fri, 30 Jul 2010 05:14:00 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=310</guid>
		<description><![CDATA[Unlike sessions, it appears that cookies are a bit of an orphaned feature in Struts2. There are ways of reading them using the framework, but no way to write them. Since you have to go directly to the ServletResponse to write cookies, you may as well use the ServletRequest directly to read them while you&#039;re [...]]]></description>
			<content:encoded><![CDATA[<p>Unlike sessions, it appears that cookies are a bit of an orphaned feature in Struts2. There are ways of reading them using the framework, but no way to write them. <span id="more-310"></span>Since you have to go directly to the ServletResponse to write cookies, you may as well use the ServletRequest directly to read them while you&#039;re at it (IMO); so that&#039;s the approach i&#039;m spruiking here.</p>
<p>Firstly, you need access to the ServletRequest and ServletResponse. To get Struts 2 to inject these into your action, you need to implement ServletRequestAware and ServletResponseAware, like so:</p>
<pre>
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;

public class MyAction extends ActionSupport implements ServletResponseAware, ServletRequestAware {

  protected HttpServletResponse servletResponse;
  @Override
  public void setServletResponse(HttpServletResponse servletResponse) {
    this.servletResponse = servletResponse;
  }

  protected HttpServletRequest servletRequest;
  @Override
  public void setServletRequest(HttpServletRequest servletRequest) {
    this.servletRequest = servletRequest;
  }
}
</pre>
<p>Once you&#039;ve got those interfaces implemented as above, you can use them as below:</p>
<pre>
public String execute() {
  int division;

  // Save to cookie
  Cookie div = new Cookie("division", String.format("%d",division));
  div.setMaxAge(60*60*24*365); // Make the cookie last a year!
  servletResponse.addCookie(div);

  // Load from cookie
  for(Cookie c : servletRequest.getCookies()) {
    if (c.getName().equals("division"))
      division=Integer.parseInt(c.getValue());
  }

  return "success";
}
</pre>
<p>This method does not require you to do any configuration in your web.xml nor struts.xml, so don&#039;t worry about those. Thanks for reading, hope this helps!</p>
<p>References:<br />
<a href="http://stackoverflow.com/questions/3350554/the-struts2-cookies-map-does-not-seem-to-be-getting-set">http://stackoverflow.com/questions/3350554/the-struts2-cookies-map-does-not-seem-to-be-getting-set</a><br />
<a href="http://omkarp.blogspot.com/2007/08/working-with-cookies-in-struts-2-part-2.html">http://omkarp.blogspot.com/2007/08/working-with-cookies-in-struts-2-part-2.html</a></p>
<style>
pre {
  padding:20px;
  background:black;
  color:white;
  line-height:100%;
  font-size:130%;
  overflow:auto;
  overflow-y:hidden;
}
</style>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=310</wfw:commentRss>
		</item>
		<item>
		<title>How to use sessions with Struts 2</title>
		<link>http://splinter.com.au/blog/?p=302</link>
		<comments>http://splinter.com.au/blog/?p=302#comments</comments>
		<pubDate>Fri, 30 Jul 2010 05:01:48 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=302</guid>
		<description><![CDATA[Luckily (unlike cookies), Struts2 comes with features to give you access to sessions without dipping into the realms of the ServletRequest / ServletResponse. So here&#039;s how to use sessions with Struts 2:

You need to make your action implement SessionAware to get access to the session:
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;

public class MyAction extends extends ActionSupport implements SessionAware {

 [...]]]></description>
			<content:encoded><![CDATA[<p>Luckily (unlike cookies), Struts2 comes with features to give you access to sessions without dipping into the realms of the ServletRequest / ServletResponse. So here&#039;s how to use sessions with Struts 2:<br />
<span id="more-302"></span><br />
You need to make your action implement SessionAware to get access to the session:</p>
<pre>import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;

public class MyAction extends extends ActionSupport implements SessionAware {

  // For SessionAware
  Map&lt;String, Object&gt; session;
  @Override
  public void setSession(Map&lt;String, Object&gt; session) {
    this.session = session;
  }

}</pre>
<p>Once you&#039;ve done that, you can access the session with code like this:</p>
<pre>public String execute() {
  int division = ...

  // Write to the session
  session.put("division",(Integer)division);

  // Read from the session
  if (session.containsKey("division"))
    division=(Integer)session.get("division");

  return "success";
}</pre>
<p>And if you feel like configuring your session lifetimes manually, make the following edits to your web.xml:</p>
<pre>&lt;web-app&gt;
  &lt;!-- How long the sessions will stick around for --&gt;
  &lt;session-config&gt;
    &lt;session-timeout&gt;600&lt;/session-timeout&gt; &lt;!-- 600seconds = 10 mins --&gt;
  &lt;/session-config&gt;
&lt;/web-app&gt;</pre>
<p>Reference:<br />
http://struts.apache.org/2.0.14/docs/how-do-we-get-access-to-the-session.html</p>
<style>
pre {
  padding:10px;
  background:black;
  color:white;
  line-height:100%;
  font-size:130%;
  overflow:auto;
  overflow-y:hidden;
}
</style>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=302</wfw:commentRss>
		</item>
		<item>
		<title>Using Quartz Scheduler in a Java web app (servlet)</title>
		<link>http://splinter.com.au/blog/?p=287</link>
		<comments>http://splinter.com.au/blog/?p=287#comments</comments>
		<pubDate>Tue, 27 Jul 2010 05:45:29 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=287</guid>
		<description><![CDATA[Lately i&#039;ve needed to run a background task every 10 seconds in a Java web-app (servlet) and found Quartz Scheduler to be great. It&#039;s just that their documentation doesn&#039;t really cover servlets much. So here we are:
I&#039;m using Struts2, Spring, and Hibernate, but most of this will apply regardless of framework you choose. Firstly, download [...]]]></description>
			<content:encoded><![CDATA[<p>Lately i&#039;ve needed to run a background task every 10 seconds in a Java web-app (servlet) and found <a href="http://quartz-scheduler.org">Quartz Scheduler</a> to be great. It&#039;s just that their documentation doesn&#039;t really cover servlets much. So here we are:<span id="more-287"></span></p>
<p>I&#039;m using Struts2, Spring, and Hibernate, but most of this will apply regardless of framework you choose. Firstly, download and unzip Quartz and copy the quartz-all-*.jar to your WebContent\WEB-INF\lib folder. Then copy all the jar&#039;s from the unzipped quartz lib folder in as well.<br />
Edit your web.xml and add the following inside the web-app section:</p>
<pre>&lt;listener>
  &lt;listener-class>org.quartz.ee.servlet.QuartzInitializerListener&lt;/listener-class>
&lt;/listener></pre>
<p>Next create a \src\quartz.properties with contents like the below:</p>
<pre># Generic configuration - probably not needed, most of this is just the defaults
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = 1
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

# Configure it to look in the quartz.xml for the job schedules
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames = quartz.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
org.quartz.plugin.jobInitializer.scanInterval = 120</pre>
<p>Then create a \src\quartz.xml similarly to below. Of course your xml will be different, but what i&#039;ve got here is a single job that repeats every 10 seconds forever, the class being &#039;background.jobs.MyJob&#039;:</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;job-scheduling-data
	xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd"
	version="1.8"&gt;

	&lt;!--
		See: http://www.quartz-scheduler.org/docs/cookbook/JobInitPlugin.html
	--&gt;

	&lt;pre-processing-commands&gt;
		&lt;delete-jobs-in-group&gt;*&lt;/delete-jobs-in-group&gt;  &lt;!-- clear all jobs in scheduler --&gt;
		&lt;delete-triggers-in-group&gt;*&lt;/delete-triggers-in-group&gt; &lt;!-- clear all triggers in scheduler --&gt;
	&lt;/pre-processing-commands&gt;

	&lt;processing-directives&gt;
		&lt;!--
			if there are any jobs/trigger in scheduler of same name (as in this
			file), overwrite them
		--&gt;
		&lt;overwrite-existing-data&gt;true&lt;/overwrite-existing-data&gt;
		&lt;!--
			if there are any jobs/trigger in scheduler of same name (as in this
			file), and over-write is false, ignore them rather then generating an
			error
		--&gt;
		&lt;ignore-duplicates&gt;false&lt;/ignore-duplicates&gt;
	&lt;/processing-directives&gt;

	&lt;!-- Ok this is where all the jobs are created: --&gt;

	&lt;schedule&gt;
		&lt;job&gt;
			&lt;name&gt;MyJob&lt;/name&gt;
			&lt;job-class&gt;background.jobs.MyJob&lt;/job-class&gt;
		&lt;/job&gt;
		&lt;trigger&gt;
			&lt;simple&gt;
				&lt;name&gt;TenSecondIntervals&lt;/name&gt;
				&lt;job-name&gt;MyJob&lt;/job-name&gt;
				&lt;repeat-count&gt;-1&lt;/repeat-count&gt; &lt;!-- repeat forever  --&gt;
				&lt;repeat-interval&gt;10000&lt;/repeat-interval&gt;  &lt;!--  every 10 seconds --&gt;
			&lt;/simple&gt;
		&lt;/trigger&gt;

	&lt;/schedule&gt;
&lt;/job-scheduling-data&gt;</pre>
<p>Finally, you want to create the actual job class with the code to be called periodically. Here&#039;s MyJob.java below. You&#039;ll notice that i&#039;m implementing StatefulJob instead of plain Job - this is so that if the job is still running when it is due to start again, Quartz won&#039;t start another instance of it:</p>
<pre>package background.jobs;

import org.quartz.*;
import org.slf4j.*;

public class MyJob implements StatefulJob {

  @Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    try {

      // Do all my stuff here

    } catch (Exception ex) {
      LoggerFactory.getLogger(getClass()).error(ex.getMessage());
    }
  }
}</pre>
<p>Good luck!</p>
<style>pre {overflow:auto; width:100%; line-height:100%; border:1px solid gray;background:#eee;}</style>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=287</wfw:commentRss>
		</item>
		<item>
		<title>Javascript date picker that Doesn&#039;t Suck!(tm)</title>
		<link>http://splinter.com.au/blog/?p=278</link>
		<comments>http://splinter.com.au/blog/?p=278#comments</comments>
		<pubDate>Tue, 27 Jul 2010 03:46:57 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=278</guid>
		<description><![CDATA[Hi,
I&#039;ve been fed up with all the JS calendar date pickers that are out there - they&#039;re all too complicated, heavy, rely on frameworks that i don&#039;t want to include, or they&#039;re ugly, or have anti-commercial licenses, etc etc etc. So here&#039;s mine:
Download the files here

You can see a demo here
Here&#039;s the pros:
* I&#039;ve tested [...]]]></description>
			<content:encoded><![CDATA[<p>Hi,<br />
I&#039;ve been fed up with all the JS calendar date pickers that are out there - they&#039;re all too complicated, heavy, rely on frameworks that i don&#039;t want to include, or they&#039;re ugly, or have anti-commercial licenses, etc etc etc. So here&#039;s mine:<span id="more-278"></span><br />
<a href="http://github.com/chrishulbert/datepicker/downloads">Download the files here</a><br />
<img src="http://splinter.com.au/blog/wp-content/uploads/datepicker.png" alt="" title="datepicker" width="252" height="255" class="alignnone size-full wp-image-279" /></p>
<p><a href="http://chrishulbert.github.com/datepicker/">You can see a demo here</a></p>
<p>Here&#039;s the pros:<br />
* I&#039;ve tested with IE, Chrome, Firefox, and Safari, and they all seem to work, miraculously.<br />
* Very light: only two extra files needed (about 10k)!<br />
* No extra frameworks needed!<br />
* Not hideously ugly! (IMO)<br />
* Date format is universal - no more confusing d/m/y with m/d/y!<br />
* Very simple to use: include two files in your head, and set a class on your input&#039;s.</p>
<p>Here&#039;s how to use it. In your page&#039;s head, add the following lines:</p>
<p>&lt;link rel=&#034;stylesheet&#034; type=&#034;text/css&#034; href=&#034;${contextPath}/assets/datepicker.css&#034; /&gt;<br />
&lt;script type=&#034;text/javascript&#034; src=&#034;${contextPath}/assets/datepicker.js&#034;&gt;&lt;/script&gt;</p>
<p>Whenever you have an input box, set the class to &#039;datepicker&#039; (or add &#039;datepicker&#039; to the list of classes), and ensure that the input box has an id, and you&#039;re done:</p>
<p>&lt;input id=&#039;start_dt&#039; class=&#039;datepicker&#039;&gt;<br />
&lt;input id=&#039;another_dt&#039; class=&#039;myclass datepicker&#039;&gt;</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=278</wfw:commentRss>
		</item>
		<item>
		<title>Using Oracle XE with Hibernate</title>
		<link>http://splinter.com.au/blog/?p=271</link>
		<comments>http://splinter.com.au/blog/?p=271#comments</comments>
		<pubDate>Tue, 20 Jul 2010 01:36:49 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=271</guid>
		<description><![CDATA[Recently had to upgrade one of my programs to go from some other database to Oracle. Luckily it uses hibernate, so i thought it would be hassle free. Well, i hit a few hiccups so here&#039;s how i finally got it to work. Note that this is for Oracle XE, but should be applicable for [...]]]></description>
			<content:encoded><![CDATA[<p>Recently had to upgrade one of my programs to go from some other database to Oracle. Luckily it uses hibernate, so i thought it would be hassle free. Well, i hit a few hiccups so here&#039;s how i finally got it to work. Note that this is for Oracle XE, but should be applicable for any Oracle version 9i onwards.<br />
<span id="more-271"></span><br />
Here&#039;s the hibernate.cfg.xml configuration for connecting to an Oracle XE. Importantly, if you&#039;re using hibernate 3.3+ you can skip the dialect setting because it&#039;ll <a href="http://docs.jboss.org/hibernate/stable/core/reference/en/html/portability.html#portability-dialectresolver">auto-detect the dialect</a> beautifully.</p>
<pre>
&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&gt;
&lt;hibernate-configuration&gt;
  &lt;session-factory&gt;
    &lt;!-- Database connection settings --&gt;
    &lt;property name="connection.driver_class"&gt;oracle.jdbc.pool.OracleDataSource&lt;/property&gt;
    &lt;property name="connection.url"&gt;jdbc:oracle:thin:@myhost:1521/xe&lt;/property&gt;
    &lt;property name="connection.username"&gt;myusername&lt;/property&gt;
    &lt;property name="connection.password"&gt;mypassword&lt;/property&gt;
    &lt;!-- etc ... !-&gt;&gt;
  &lt;/session-factory&gt;
&lt;/hibernate-configuration&gt;
</pre>
<p>If you don&#039;t trust the dialect autodetection, you can prove it&#039;s working by looking in the console log to see which dialect it chose. Here&#039;s what it chose for me, using Oracle XE. It looks like its guess was spot on:</p>
<pre>
2515 [main] INFO  org.hibernate.dialect.Dialect  -
  Using dialect: org.hibernate.dialect.Oracle10gDialect
</pre>
<p>Now for drivers. You really need the latest JDBC type 4 driver you can find, because the older ones are buggy. For instance, i was getting the following error with an older driver: &#034;SEVERE: Error while registering Oracle JDBC Diagnosability MBean&#034;.<br />
Here&#039;s where you can find the latest <a href="http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html">Oracle JDBC drivers</a>.<br />
Even though i&#039;m using Oracle XE which is 10g, i downloaded the latest 11g driver. You only need either ojdbc5.jar (for java 5) or ojdbc6.jar (Java 6) according to which version of Java you&#039;re deploying to.</p>
<p>So copy your ojdbc5/6.jar to your WebContent/WEB-INF/lib folder and you&#039;ll be good to go! No other client is needed.</p>
<p>Good luck</p>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=271</wfw:commentRss>
		</item>
		<item>
		<title>A simple implementation of AES in Ruby from scratch</title>
		<link>http://splinter.com.au/blog/?p=265</link>
		<comments>http://splinter.com.au/blog/?p=265#comments</comments>
		<pubDate>Tue, 29 Jun 2010 05:52:11 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=265</guid>
		<description><![CDATA[How to implement 128 bit AES / Rijndael from scratch: A simple Ruby implementation with lots of comments. This document explains how to implement AES, from the perspective of learning how the algorithm works. Simplicity and learnability are favoured over optimisation unlike most open source crypto code, so hopefully this will make it easier for [...]]]></description>
			<content:encoded><![CDATA[<p>How to implement 128 bit AES / Rijndael from scratch: A simple Ruby implementation with lots of comments. This document explains how to implement AES, from the perspective of learning how the algorithm works.<span id="more-265"></span> Simplicity and learnability are favoured over optimisation unlike most open source crypto code, so hopefully this will make it easier for people to understand and de-mystify AES and block-cipher cryptography in general.</p>
<p>Code can be found here: <a href="http://github.com/chrishulbert/crypto">http://github.com/chrishulbert/crypto</a></p>
<p><a title="View How to Implement AES in Ruby on Scribd" href="http://www.scribd.com/doc/33686967/How-to-Implement-AES-in-Ruby" style="margin: 12px auto 6px auto; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; -x-system-font: none; display: block; text-decoration: underline;">How to Implement AES in Ruby</a> <object id="doc_686863448207766" name="doc_686863448207766" height="500" width="100%" type="application/x-shockwave-flash" data="http://d1.scribdassets.com/ScribdViewer.swf" style="outline:none;" rel="media:document" resource="http://d1.scribdassets.com/ScribdViewer.swf?document_id=33686967&#038;access_key=key-19s8x0v8k4rf2pw6zvt5&#038;page=1&#038;viewMode=list" xmlns:media="http://search.yahoo.com/searchmonkey/media/" xmlns:dc="http://purl.org/dc/terms/" ><param name="movie" value="http://d1.scribdassets.com/ScribdViewer.swf"><param name="wmode" value="opaque"><param name="bgcolor" value="#ffffff"><param name="allowFullScreen" value="true"><param name="allowScriptAccess" value="always"><param name="FlashVars" value="document_id=33686967&#038;access_key=key-19s8x0v8k4rf2pw6zvt5&#038;page=1&#038;viewMode=list"><embed id="doc_686863448207766" name="doc_686863448207766" src="http://d1.scribdassets.com/ScribdViewer.swf?document_id=33686967&#038;access_key=key-19s8x0v8k4rf2pw6zvt5&#038;page=1&#038;viewMode=list" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="500" width="100%" wmode="opaque" bgcolor="#ffffff"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=265</wfw:commentRss>
		</item>
		<item>
		<title>Asp.Net Forms authentication to your own database</title>
		<link>http://splinter.com.au/blog/?p=264</link>
		<comments>http://splinter.com.au/blog/?p=264#comments</comments>
		<pubDate>Fri, 28 May 2010 02:05:57 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=264</guid>
		<description><![CDATA[Hi all, here&#039;s the quickest way to get forms authentication up and running in your asp.net web app, storing usernames and hashed password in your own database.

First up, make a table in your database to store your users:
CREATE TABLE users(
	email [varchar](250)  NOT NULL,
	password [varchar](250) NOT NULL,
  CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED
  (
 [...]]]></description>
			<content:encoded><![CDATA[<p>Hi all, here&#039;s the quickest way to get forms authentication up and running in your asp.net web app, storing usernames and hashed password in your own database.<br />
<span id="more-264"></span><br />
First up, make a table in your database to store your users:</p>
<pre>CREATE TABLE users(
	email [varchar](250)  NOT NULL,
	password [varchar](250) NOT NULL,
  CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED
  (
    [email] ASC
  )
)</pre>
<p>Then, edit your web.config like this (i&#039;ve skipped the bits you don&#039;t need to change):</p>
<pre>&lt;configuration&gt;
  &lt;connectionStrings&gt;
    &lt;add name="Main" connectionString="uid=myuser; pwd=mypassword; Initial Catalog=mydatabase; Server=mydbserver" /&gt;
  &lt;/connectionStrings&gt;

  &lt;system.web&gt;
    &lt;authentication mode="Forms" /&gt;
    &lt;authorization&gt;
      &lt;deny users="?" /&gt;
    &lt;/authorization&gt;
  &lt;/system.web&gt;

  &lt;location path="Assets"&gt;
    &lt;system.web&gt;
      &lt;authorization&gt;
        &lt;allow users="*" /&gt;
      &lt;/authorization&gt;
    &lt;/system.web&gt;
  &lt;/location&gt;
&lt;/configuration&gt;</pre>
<p>The connection string is what we&#039;ll use to get access to our sql server. The &#039;authentication mode=forms&#039; bit is what enables the forms auth mode. The &#039;deny users=?&#039; is used so that people who haven&#039;t logged in will have no access. And the &#039;location path=assets&#039; part is so that when you&#039;re not logged in, you&#039;ll still be able to see all the assets, eg your CSS, images, etc so that the login page doesn&#039;t look hideous.</p>
<p>Next up, make a typical web form called Login.aspx, with the following controls in it somewhere:</p>
<pre>&lt;p&gt;
  &lt;strong&gt;Email:&lt;/strong&gt;
  &lt;br /&gt;
  &lt;asp:TextBox ID="txtUserName" runat="server" Columns=50&gt;&lt;/asp:TextBox&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;strong&gt;Password:&lt;/strong&gt;
  &lt;br /&gt;
  &lt;asp:TextBox ID="txtUserPass" runat="server" Columns=50 TextMode="Password"&gt;&lt;/asp:TextBox&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;asp:Button ID="bnLogon" runat="server" Text="Logon" /&gt;
  &lt;br /&gt;
  &lt;asp:Label id="lblMsg" ForeColor="red" runat="server" /&gt;
&lt;/p&gt;</pre>
<p>Go into design mode, and double click the logon button to make an event handler for it in the code-behind file. Your login.aspx.cs should look like this:</p>
<pre>using System;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;
using System.Data.SqlClient;
using System.Security.Cryptography;

public partial class Login : System.Web.UI.Page
{
  protected void Page_Load(object sender, EventArgs e)
  {
    if (!String.IsNullOrEmpty(Request.Params["logout"]))
    {
      FormsAuthentication.SignOut();
      Response.Redirect("./");
    }
  }

  protected void bnLogon_Click(object sender, EventArgs e)
  {
    if (ValidateUser(txtUserName.Text, txtUserPass.Text))
      FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, true);
    else
      lblMsg.Text = "Incorrect";
  }

  /// &lt;summary&gt;
  /// Filter out the fat fingers who get their passwords wrong
  /// &lt;/summary&gt;
  bool ValidateUser(string user, string pass)
  {
    string connStr = ConfigurationManager.ConnectionStrings["Main"].ConnectionString;
    using (SqlConnection conn = new SqlConnection(connStr))
    {
      conn.Open();
      string sql = "select email from users where email = @email and password = @password";
      SqlCommand cmd = new SqlCommand(sql, conn);
      cmd.Parameters.AddWithValue("@email", user);
      cmd.Parameters.AddWithValue("@password", Sha1(Salt(pass)));
      return cmd.ExecuteScalar() is string;
    }
  }

  /// &lt;summary&gt;
  /// Salt the hell out of a string before hashing it
  /// &lt;/summary&gt;
  public string Salt(string text)
  {
    return
      "zu5QnKrH4NJfOgV2WWqV5Oc1l" +
      text +
      "1DMuByokGSDyFPQ0DbXd9rAgW";
  }

  /// &lt;summary&gt;
  /// One-way hash the password, so the DBA can't see the inevitable swear words in the password column
  /// &lt;/summary&gt;
  public string Sha1(string text)
  {
    byte[] clear = Encoding.UTF8.GetBytes(text);
    byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(clear);
    return BitConverter.ToString(hash).Replace("-", "").ToLower();
  }
}</pre>
<p>Make sure you change the salt values above, just to make it your own.<br />
Now, in your master page you probably want somewhere on your page to show the current logged in name, and provide a logout link. For me, it looks like this:</p>
<pre>&lt;% if (Context.User.Identity.IsAuthenticated) { %&gt;
  &lt;%= Context.User.Identity.Name%&gt;
  &lt;a href="Login.aspx?logout=y"&gt;Log out&lt;/a&gt;
&lt;% } %&gt;</pre>
<p>Ok we&#039;re done with it! But now you&#039;ll need to create at least one user in your database, and probably more. To create the hashed password, i used the following ruby script (make sure your salts are the same as what is in your login.aspx).</p>
<pre>a="zu5QnKrH4NJfOgV2WWqV5Oc1l"
b="1DMuByokGSDyFPQ0DbXd9rAgW"
c=a + "my new password here" + b
require 'digest/sha1'
Digest::SHA1.hexdigest c
=&gt; "e7f0df4d064a7d2cdc653447e752cf4d736e114b"</pre>
<p>Now, you can put that value between the quites in your &#039;password&#039; field in the database.</p>
<p>Cheers!</p>
<style>pre { line-height:100%; overflow:auto; }</style>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=264</wfw:commentRss>
		</item>
		<item>
		<title>AS2805 (like ISO8583) financial message parser in C#</title>
		<link>http://splinter.com.au/blog/?p=256</link>
		<comments>http://splinter.com.au/blog/?p=256#comments</comments>
		<pubDate>Fri, 07 May 2010 01:47:30 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=256</guid>
		<description><![CDATA[Binary message parsing! This is one of those areas that I must admit, C *almost* has the edge on C#.
Anyway, here&#039;s an AS2805 message parser written in C#. 
This code will parse an AS2805 message. With some modifications, I&#039;m sure you could make it work with ISO8583 too.

using System;
using System.Text;
using System.Reflection;

class As2805Message
{
  #region Header [...]]]></description>
			<content:encoded><![CDATA[<p>Binary message parsing! This is one of those areas that I must admit, C *almost* has the edge on C#.<br />
Anyway, here&#039;s an AS2805 message parser written in C#. <span id="more-256"></span><br />
This code will parse an AS2805 message. With some modifications, I&#039;m sure you could make it work with ISO8583 too.</p>
<pre>
using System;
using System.Text;
using System.Reflection;

class As2805Message
{
  #region Header fields and Data Element variables
  public int mti = 0;
  public byte[] primaryBitmap = { 0, 0, 0, 0, 0, 0, 0, 0 };
  public byte[] de1_SecondaryBitmap = { 0, 0, 0, 0, 0, 0, 0, 0 };	// DE1: Bitmap, extended (b64)
  public string de2_PAN;	// DE2: PAN (n19)
  public string de3_ProcCode;	// DE3: Processing Code (n6)
  public string de4_AmtTxn;	// DE4: Amount, Txn (n12)
  public string de5_AmtSettle;	// DE5: Amount, Settlement (n12)
  public string de6_AmtCardhBill;	// DE6: Amount,Cardholder billing (n12)
  public string de7_TransDttm;	// DE7: Transmission date and time (n10)
  public string de8_AmtCardhBillFee;	// DE8: Amount, Cardholder billing fee (n8 )
  public string de9_ConvRateSettle;	// DE9: Conversion rate, settlement (n8 )
  public string de10_ConvRateCardhBill;	// DE10: Conversion rate, cardh bill (n8 )
  public string de11_STAN;	// DE11: STAN (n6)
  public string de12_TimeLocal;	// DE12: Time, Local txn (n6)
  public string de13_DateLocal;	// DE13: Date, Local txn (n4)
  public string de14_DateExpiry;	// DE14: Date, expiry (n4)
  public string de15_DateSetl;	// DE15: Date, Settlement (n4)
  public string de16_DateConv;	// DE16: Date, Conversion (n4)
  public string de17_DateCapt;	// DE17: Date, Capture (n4)
  public string de18_MerchType;	// DE18: Merchants type (n4)
  public string de19_AcqInstCtryCode;	// DE19: Acquiring Inst Count code (n3)
  public string de20_PriAccNumExtCtryCode;	// DE20: Prim Acc Num Ext, Count Code (n3)
  public string de21_FwdInstCtryCode;	// DE21: Forwarding inst count code (n3)
  public string de22_PosEntryMode;	// DE22: POS entry mode (n3)
  public string de23_CardSeqNo;	// DE23: Card Seq No (n3)
  public string de24_NetIntlId;	// DE24: Net Intl Id (n3)
  public string de25_PosCondCode;	// DE25: POS  Cond Code (n2)
  public string de26_PosPinCaptCode;	// DE26: POS PIN Capture code (n2)
  public string de27_AuthIdRespLen;	// DE27: Auth Ident Resp Len (n1)
  public string de28_AmtTxnFee;	// DE28: Amount, Txn fee (xn8 )
  public string de29_AmtSettleFee;	// DE29: Amount, Settlment fee (xn8 )
  public string de30_AmtTxnProcFee;	// DE30: Amount, Txn Proc Fee (xn8 )
  public string de31_AmtSettleProcFee;	// DE31: Amount, Setl Proc Fee (xn8 )
  public string de32_AcqInstIdCode;	// DE32: Acq Inst Id Code (n11)
  public string de33_FwdInstIdCode;	// DE33: Fwd Inst Id code (n11)
  public string de34_PanExt;	// DE34: PAN, Extended (ns28 )
  public byte[] de35_Track2;	// DE35: Track 2 (z37)
  public byte[] de36_Track3;	// DE36: Track 3 (z104)
  public string de37_RetRefNo;	// DE37: RRN (an12)
  public string de38_AuthIdentResp;	// DE38: Auth identification response (an6)
  public string de39_RespCode;	// DE39: Response code (an2)
  public string de40_ServRestrCode;	// DE40: Service Restr Code (an3)
  public string de41_CardAcptTermId;	// DE41: TID (orig ans) (an8 )
  public string de42_CardAcptIdCode;	// DE42: Card Acceptor Identification Code (orig ans) (an15)
  public string de43_CardAcptNameLoc;	// DE43: Card acpt name/loc (orig ans) (an40)
  public byte[] de44_AddtRespData;	// DE44: Addtl resp data (ans25)
  public byte[] de45_Track1;	// DE45: Track 1 (ans76)
  public byte[] de46_AddtlDataIso;	// DE46: Addtl data - ISO (ans999)
  public byte[] de47_AddtlDataNat;	// DE47: Addtl data - National (ans999)
  public byte[] de48_AddtlDataPriv;	// DE48: Addtl data - Private (ans999)
  public string de49_CurCodeTxn;	// DE49: Cur code, txn (a or n3)
  public string de50_CurCodeSettle;	// DE50: Cur code, setl (a or n3)
  public string de51_CurCodeCardhBill;	// DE51: Cur code, cardh bill (a or n3)
  public byte[] de52_PinData;	// DE52: Pin data (b64)
  public string de53_SecControlInfo;	// DE53: Security related control info (n16)
  public string de54_AddtlAmts;	// DE54: Additional amounts (an120)
  public byte[] de55_ResIso;	// DE55: ICC Data (Reserved ISO) (ans999)
  public byte[] de56_ResIso;	// DE56: Reserved ISO (ans999)
  public string de57_AmtCash;	// DE57: Amount, Cash oz only (n12)
  public string de58_BalanceLedger;	// DE58: Ledger balance oz only (n12)
  public string de59_BalanceCleared;	// DE59: Acct balance, cleared funds oz only (n12)
  public string de60_PreswipeStatus;	// DE60: Preswipe status (Reserved Private, normally ans 999) (an1)
  public byte[] de61_ResPriv;	// DE61: Reserved Private (ans999)
  public byte[] de62_ResPriv;	// DE62: Reserved Private (ans999)
  public byte[] de63_ResPriv;	// DE63: Reserved Private (ans999)
  public byte[] de64_MAC;	// DE64: MAC (b64)
  public byte[] de65_Bitmap;	// DE65: Bit map, extended 2 (b64)
  public string de66_SettleCode;	// DE66: Settlement code (n1)
  public string de67_ExtPayCode;	// DE67: Ext payment code (n2)
  public string de68_RecvInstCtryCode;	// DE68: Recv inst count code (n3)
  public string de69_SettleInstCtryCode;	// DE69: Setl Inst Count code (n3)
  public string de70_NetMgtInfoCode;	// DE70: Net mgt info code (n3)
  public string de71_MessageNo;	// DE71: Message No (n4)
  public string de72_MessageNoLast;	// DE72: Message No Last (n4)
  public string de73_DateAction;	// DE73: Date, Action (n6)
  public string de74_CreditsNo;	// DE74: Credits, Num (n10)
  public string de75_CreditRevsNo;	// DE75: Credit revs, num (n10)
  public string de76_DebitsNo;	// DE76: Debits, num (n10)
  public string de77_DebitRevsNo;	// DE77: Debit revs, num (n10)
  public string de78_TransfersNo;	// DE78: Transfers, num (n10)
  public string de79_TransferRevsNo;	// DE79: Transfer revs, num (n10)
  public string de80_InquiriesNo;	// DE80: Inquiries, num (n10)
  public string de81_AuthsNo;	// DE81: Auths, num (n10)
  public string de82_CreditsProcFeeAmt;	// DE82: Credits, proc fee amt (n12)
  public string de83_CreditsTxnFeeAmt;	// DE83: Credits, transaction fee amt (n12)
  public string de84_DebitsProcFeeAmt;	// DE84: Debits, proc fee amt (n12)
  public string de85_DebitsTxnFeeAmt;	// DE85: Debits, transaction fee amt (n12)
  public string de86_CreditsAmt;	// DE86: Credits, amt (n16)
  public string de87_CreditRevsAmt;	// DE87: Credit revs, amt (n16)
  public string de88_DebitsAmt;	// DE88: Debits, amt (n16)
  public string de89_DebitRevsAmt;	// DE89: Debit revs, amount (n16)
  public string de90_OrigDataElem;	// DE90: Original data elements (n42)
  public string de91_FileUpdateCode;	// DE91: File update code (an1)
  public string de92_FileSecCode;	// DE92: File security code (an2)
  public string de93_RespInd;	// DE93: Response indicator (an5)
  public string de94_ServInd;	// DE94: Service indicator (an7)
  public string de95_ReplAmts;	// DE95: Replacement amounts (an42)
  public byte[] de96_MsgSecCode;	// DE96: Message Security code (b64)
  public string de97_AmtNetSetl;	// DE97: Amount, net settlement (xn16)
  public byte[] de98_Payee;	// DE98: Payee (ans25)
  public string de99_SettleInstIdCode;	// DE99: Setl inst id code (n11)
  public string de100_RecvInstIdCode;	// DE100: Recv inst id code (n11)
  public string de101_FileName;	// DE101: File name (normally ans) (an17)
  public byte[] de102_AcctId1;	// DE102: Account id 1 (ans28 )
  public byte[] de103_AcctId2;	// DE103: Account id 2 (ans28 )
  public byte[] de104_TxnDesc;	// DE104: Txn description (ans100)
  public byte[] de105_ResvIso;	// DE105: Reserved for iso use (ans999)
  public byte[] de106_ResvIso;	// DE106: Reserved for iso use (ans999)
  public byte[] de107_ResvIso;	// DE107: Reserved for iso use (ans999)
  public byte[] de108_ResvIso;	// DE108: Reserved for iso use (ans999)
  public byte[] de109_ResvIso;	// DE109: Reserved for iso use (ans999)
  public byte[] de110_ResvIso;	// DE110: Reserved for iso use (ans999)
  public byte[] de111_ResvIso;	// DE111: Reserved for iso use (ans999)
  public byte[] de112_ResvNat;	// DE112: Reserved for national use (ans999)
  public byte[] de113_ResvNat;	// DE113: Reserved for national use (ans999)
  public byte[] de114_ResvNat;	// DE114: Reserved for national use (ans999)
  public byte[] de115_ResvNat;	// DE115: Reserved for national use (ans999)
  public byte[] de116_ResvNat;	// DE116: Reserved for national use (ans999)
  public string de117_CardStatUpdCode;	// DE117: Card status update code (oz only) (an2)
  public string de118_TotalCashNo;	// DE118: Cash, total number oz only (n10)
  public string de119_TotalCashAmt;	// DE119: Cash, total amount oz only (n16)
  public byte[] de120_ResvPriv;	// DE120: Reserved for private use (ans999)
  public byte[] de121_ResvPriv;	// DE121: Reserved for private use (ans999)
  public byte[] de122_ResvPriv;	// DE122: Reserved for private use (ans999)
  public byte[] de123_ResvPriv;	// DE123: Reserved for private use (ans999)
  public byte[] de124_ResvPriv;	// DE124: Reserved for private use (ans999)
  public byte[] de125_ResvPriv;	// DE125: Reserved for private use (ans999)
  public byte[] de126_ResvPriv;	// DE126: Reserved for private use (ans999)
  public byte[] de127_ResvPriv;	// DE127: Reserved for private use (ans999)
  public byte[] de128_MAC;	// DE128: MAC (b64)
  #endregion

  #region Parser
  /// &lt;summary&gt;
  /// Creates an As2805Message object by parsing the given message.
  /// The message should start with the first 2 bytes of the MTI.
  /// &lt;/summary&gt;
  public As2805Message(byte[] buf)
  {
    // Read the header fields
    mti =
      (buf[0] &gt;&gt; 4) * 1000 +
      (buf[0] &#038; 0xf) * 100 +
      (buf[1] &gt;&gt; 4) * 10 +
      (buf[1] &#038; 0xf);
    primaryBitmap = B(buf, 2, 8 );

    // This keeps track of how much of the buffer we've read so far
    int offset = 10;

    // Read the fields according to which ones are set in the bitmap
    if ((primaryBitmap[0] &#038; 128 ) &gt; 0) de1_SecondaryBitmap = ReadFixRaw(buf, ref offset, 8 );
    if ((primaryBitmap[0] &#038; 64) &gt; 0) de2_PAN = ReadVar2Packed(buf, ref offset, 19);
    if ((primaryBitmap[0] &#038; 32) &gt; 0) de3_ProcCode = ReadFixPacked(buf, ref offset, 6);
    if ((primaryBitmap[0] &#038; 16) &gt; 0) de4_AmtTxn = ReadFixPacked(buf, ref offset, 12);
    if ((primaryBitmap[0] &#038; 8 ) &gt; 0) de5_AmtSettle = ReadFixPacked(buf, ref offset, 12);
    if ((primaryBitmap[0] &#038; 4) &gt; 0) de6_AmtCardhBill = ReadFixPacked(buf, ref offset, 12);
    if ((primaryBitmap[0] &#038; 2) &gt; 0) de7_TransDttm = ReadFixPacked(buf, ref offset, 10);
    if ((primaryBitmap[0] &#038; 1) &gt; 0) de8_AmtCardhBillFee = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[1] &#038; 128 ) &gt; 0) de9_ConvRateSettle = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[1] &#038; 64) &gt; 0) de10_ConvRateCardhBill = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[1] &#038; 32) &gt; 0) de11_STAN = ReadFixPacked(buf, ref offset, 6);
    if ((primaryBitmap[1] &#038; 16) &gt; 0) de12_TimeLocal = ReadFixPacked(buf, ref offset, 6);
    if ((primaryBitmap[1] &#038; 8 ) &gt; 0) de13_DateLocal = ReadFixPacked(buf, ref offset, 4);
    if ((primaryBitmap[1] &#038; 4) &gt; 0) de14_DateExpiry = ReadFixPacked(buf, ref offset, 4);
    if ((primaryBitmap[1] &#038; 2) &gt; 0) de15_DateSetl = ReadFixPacked(buf, ref offset, 4);
    if ((primaryBitmap[1] &#038; 1) &gt; 0) de16_DateConv = ReadFixPacked(buf, ref offset, 4);
    if ((primaryBitmap[2] &#038; 128 ) &gt; 0) de17_DateCapt = ReadFixPacked(buf, ref offset, 4);
    if ((primaryBitmap[2] &#038; 64) &gt; 0) de18_MerchType = ReadFixPacked(buf, ref offset, 4);
    if ((primaryBitmap[2] &#038; 32) &gt; 0) de19_AcqInstCtryCode = ReadFixPacked(buf, ref offset, 3);
    if ((primaryBitmap[2] &#038; 16) &gt; 0) de20_PriAccNumExtCtryCode = ReadFixPacked(buf, ref offset, 3);
    if ((primaryBitmap[2] &#038; 8 ) &gt; 0) de21_FwdInstCtryCode = ReadFixPacked(buf, ref offset, 3);
    if ((primaryBitmap[2] &#038; 4) &gt; 0) de22_PosEntryMode = ReadFixPacked(buf, ref offset, 3);
    if ((primaryBitmap[2] &#038; 2) &gt; 0) de23_CardSeqNo = ReadFixPacked(buf, ref offset, 3);
    if ((primaryBitmap[2] &#038; 1) &gt; 0) de24_NetIntlId = ReadFixPacked(buf, ref offset, 3);
    if ((primaryBitmap[3] &#038; 128 ) &gt; 0) de25_PosCondCode = ReadFixPacked(buf, ref offset, 2);
    if ((primaryBitmap[3] &#038; 64) &gt; 0) de26_PosPinCaptCode = ReadFixPacked(buf, ref offset, 2);
    if ((primaryBitmap[3] &#038; 32) &gt; 0) de27_AuthIdRespLen = ReadFixPacked(buf, ref offset, 1);
    if ((primaryBitmap[3] &#038; 16) &gt; 0) de28_AmtTxnFee = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[3] &#038; 8 ) &gt; 0) de29_AmtSettleFee = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[3] &#038; 4) &gt; 0) de30_AmtTxnProcFee = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[3] &#038; 2) &gt; 0) de31_AmtSettleProcFee = ReadFixPacked(buf, ref offset, 8 );
    if ((primaryBitmap[3] &#038; 1) &gt; 0) de32_AcqInstIdCode = ReadVar2Packed(buf, ref offset, 11);
    if ((primaryBitmap[4] &#038; 128 ) &gt; 0) de33_FwdInstIdCode = ReadVar2Packed(buf, ref offset, 11);
    if ((primaryBitmap[4] &#038; 64) &gt; 0) de34_PanExt = S(ReadVar2Raw(buf, ref offset, 28 ));
    if ((primaryBitmap[4] &#038; 32) &gt; 0) de35_Track2 = ReadVar2Raw(buf, ref offset, 37);
    if ((primaryBitmap[4] &#038; 16) &gt; 0) de36_Track3 = ReadVar3Raw(buf, ref offset, 104);
    if ((primaryBitmap[4] &#038; 8 ) &gt; 0) de37_RetRefNo = S(ReadFixRaw(buf, ref offset, 12));
    if ((primaryBitmap[4] &#038; 4) &gt; 0) de38_AuthIdentResp = S(ReadFixRaw(buf, ref offset, 6));
    if ((primaryBitmap[4] &#038; 2) &gt; 0) de39_RespCode = S(ReadFixRaw(buf, ref offset, 2));
    if ((primaryBitmap[4] &#038; 1) &gt; 0) de40_ServRestrCode = S(ReadFixRaw(buf, ref offset, 3));
    if ((primaryBitmap[5] &#038; 128 ) &gt; 0) de41_CardAcptTermId = S(ReadFixRaw(buf, ref offset, 8 ));
    if ((primaryBitmap[5] &#038; 64) &gt; 0) de42_CardAcptIdCode = S(ReadFixRaw(buf, ref offset, 15));
    if ((primaryBitmap[5] &#038; 32) &gt; 0) de43_CardAcptNameLoc = S(ReadFixRaw(buf, ref offset, 40));
    if ((primaryBitmap[5] &#038; 16) &gt; 0) de44_AddtRespData = ReadVar2Raw(buf, ref offset, 25);
    if ((primaryBitmap[5] &#038; 8 ) &gt; 0) de45_Track1 = ReadVar2Raw(buf, ref offset, 76);
    if ((primaryBitmap[5] &#038; 4) &gt; 0) de46_AddtlDataIso = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[5] &#038; 2) &gt; 0) de47_AddtlDataNat = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[5] &#038; 1) &gt; 0) de48_AddtlDataPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[6] &#038; 128 ) &gt; 0) de49_CurCodeTxn = S(ReadFixRaw(buf, ref offset, 3));
    if ((primaryBitmap[6] &#038; 64) &gt; 0) de50_CurCodeSettle = S(ReadFixRaw(buf, ref offset, 3));
    if ((primaryBitmap[6] &#038; 32) &gt; 0) de51_CurCodeCardhBill = S(ReadFixRaw(buf, ref offset, 3));
    if ((primaryBitmap[6] &#038; 16) &gt; 0) de52_PinData = ReadFixRaw(buf, ref offset, 8 );
    if ((primaryBitmap[6] &#038; 8 ) &gt; 0) de53_SecControlInfo = ReadFixPacked(buf, ref offset, 16);
    if ((primaryBitmap[6] &#038; 4) &gt; 0) de54_AddtlAmts = S(ReadVar3Raw(buf, ref offset, 120));
    if ((primaryBitmap[6] &#038; 2) &gt; 0) de55_ResIso = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[6] &#038; 1) &gt; 0) de56_ResIso = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[7] &#038; 128 ) &gt; 0) de57_AmtCash = ReadFixPacked(buf, ref offset, 12);
    if ((primaryBitmap[7] &#038; 64) &gt; 0) de58_BalanceLedger = ReadFixPacked(buf, ref offset, 12);
    if ((primaryBitmap[7] &#038; 32) &gt; 0) de59_BalanceCleared = ReadFixPacked(buf, ref offset, 12);
    if ((primaryBitmap[7] &#038; 16) &gt; 0) de60_PreswipeStatus = S(ReadVar3Raw(buf, ref offset, 1));
    if ((primaryBitmap[7] &#038; 8 ) &gt; 0) de61_ResPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[7] &#038; 4) &gt; 0) de62_ResPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[7] &#038; 2) &gt; 0) de63_ResPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((primaryBitmap[7] &#038; 1) &gt; 0) de64_MAC = ReadFixRaw(buf, ref offset, 8 );
    if ((de1_SecondaryBitmap[0] &#038; 128 ) &gt; 0) de65_Bitmap = ReadFixRaw(buf, ref offset, 8 );
    if ((de1_SecondaryBitmap[0] &#038; 64) &gt; 0) de66_SettleCode = ReadFixPacked(buf, ref offset, 1);
    if ((de1_SecondaryBitmap[0] &#038; 32) &gt; 0) de67_ExtPayCode = ReadFixPacked(buf, ref offset, 2);
    if ((de1_SecondaryBitmap[0] &#038; 16) &gt; 0) de68_RecvInstCtryCode = ReadFixPacked(buf, ref offset, 3);
    if ((de1_SecondaryBitmap[0] &#038; 8 ) &gt; 0) de69_SettleInstCtryCode = ReadFixPacked(buf, ref offset, 3);
    if ((de1_SecondaryBitmap[0] &#038; 4) &gt; 0) de70_NetMgtInfoCode = ReadFixPacked(buf, ref offset, 3);
    if ((de1_SecondaryBitmap[0] &#038; 2) &gt; 0) de71_MessageNo = ReadFixPacked(buf, ref offset, 4);
    if ((de1_SecondaryBitmap[0] &#038; 1) &gt; 0) de72_MessageNoLast = ReadFixPacked(buf, ref offset, 4);
    if ((de1_SecondaryBitmap[1] &#038; 128 ) &gt; 0) de73_DateAction = ReadFixPacked(buf, ref offset, 6);
    if ((de1_SecondaryBitmap[1] &#038; 64) &gt; 0) de74_CreditsNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[1] &#038; 32) &gt; 0) de75_CreditRevsNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[1] &#038; 16) &gt; 0) de76_DebitsNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[1] &#038; 8 ) &gt; 0) de77_DebitRevsNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[1] &#038; 4) &gt; 0) de78_TransfersNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[1] &#038; 2) &gt; 0) de79_TransferRevsNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[1] &#038; 1) &gt; 0) de80_InquiriesNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[2] &#038; 128 ) &gt; 0) de81_AuthsNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[2] &#038; 64) &gt; 0) de82_CreditsProcFeeAmt = ReadFixPacked(buf, ref offset, 12);
    if ((de1_SecondaryBitmap[2] &#038; 32) &gt; 0) de83_CreditsTxnFeeAmt = ReadFixPacked(buf, ref offset, 12);
    if ((de1_SecondaryBitmap[2] &#038; 16) &gt; 0) de84_DebitsProcFeeAmt = ReadFixPacked(buf, ref offset, 12);
    if ((de1_SecondaryBitmap[2] &#038; 8 ) &gt; 0) de85_DebitsTxnFeeAmt = ReadFixPacked(buf, ref offset, 12);
    if ((de1_SecondaryBitmap[2] &#038; 4) &gt; 0) de86_CreditsAmt = ReadFixPacked(buf, ref offset, 16);
    if ((de1_SecondaryBitmap[2] &#038; 2) &gt; 0) de87_CreditRevsAmt = ReadFixPacked(buf, ref offset, 16);
    if ((de1_SecondaryBitmap[2] &#038; 1) &gt; 0) de88_DebitsAmt = ReadFixPacked(buf, ref offset, 16);
    if ((de1_SecondaryBitmap[3] &#038; 128 ) &gt; 0) de89_DebitRevsAmt = ReadFixPacked(buf, ref offset, 16);
    if ((de1_SecondaryBitmap[3] &#038; 64) &gt; 0) de90_OrigDataElem = ReadFixPacked(buf, ref offset, 42);
    if ((de1_SecondaryBitmap[3] &#038; 32) &gt; 0) de91_FileUpdateCode = S(ReadFixRaw(buf, ref offset, 1));
    if ((de1_SecondaryBitmap[3] &#038; 16) &gt; 0) de92_FileSecCode = S(ReadFixRaw(buf, ref offset, 2));
    if ((de1_SecondaryBitmap[3] &#038; 8 ) &gt; 0) de93_RespInd = S(ReadFixRaw(buf, ref offset, 5));
    if ((de1_SecondaryBitmap[3] &#038; 4) &gt; 0) de94_ServInd = S(ReadFixRaw(buf, ref offset, 7));
    if ((de1_SecondaryBitmap[3] &#038; 2) &gt; 0) de95_ReplAmts = S(ReadFixRaw(buf, ref offset, 42));
    if ((de1_SecondaryBitmap[3] &#038; 1) &gt; 0) de96_MsgSecCode = ReadFixRaw(buf, ref offset, 8 );
    if ((de1_SecondaryBitmap[4] &#038; 128 ) &gt; 0) de97_AmtNetSetl = ReadFixPacked(buf, ref offset, 16);
    if ((de1_SecondaryBitmap[4] &#038; 64) &gt; 0) de98_Payee = ReadVar2Raw(buf, ref offset, 25);
    if ((de1_SecondaryBitmap[4] &#038; 32) &gt; 0) de99_SettleInstIdCode = ReadVar2Packed(buf, ref offset, 11);
    if ((de1_SecondaryBitmap[4] &#038; 16) &gt; 0) de100_RecvInstIdCode = ReadVar2Packed(buf, ref offset, 11);
    if ((de1_SecondaryBitmap[4] &#038; 8 ) &gt; 0) de101_FileName = S(ReadVar2Raw(buf, ref offset, 17));
    if ((de1_SecondaryBitmap[4] &#038; 4) &gt; 0) de102_AcctId1 = ReadVar2Raw(buf, ref offset, 28 );
    if ((de1_SecondaryBitmap[4] &#038; 2) &gt; 0) de103_AcctId2 = ReadVar2Raw(buf, ref offset, 28 );
    if ((de1_SecondaryBitmap[4] &#038; 1) &gt; 0) de104_TxnDesc = ReadVar3Raw(buf, ref offset, 100);
    if ((de1_SecondaryBitmap[5] &#038; 128 ) &gt; 0) de105_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 64) &gt; 0) de106_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 32) &gt; 0) de107_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 16) &gt; 0) de108_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 8 ) &gt; 0) de109_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 4) &gt; 0) de110_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 2) &gt; 0) de111_ResvIso = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[5] &#038; 1) &gt; 0) de112_ResvNat = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[6] &#038; 128 ) &gt; 0) de113_ResvNat = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[6] &#038; 64) &gt; 0) de114_ResvNat = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[6] &#038; 32) &gt; 0) de115_ResvNat = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[6] &#038; 16) &gt; 0) de116_ResvNat = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[6] &#038; 8 ) &gt; 0) de117_CardStatUpdCode = S(ReadFixRaw(buf, ref offset, 2));
    if ((de1_SecondaryBitmap[6] &#038; 4) &gt; 0) de118_TotalCashNo = ReadFixPacked(buf, ref offset, 10);
    if ((de1_SecondaryBitmap[6] &#038; 2) &gt; 0) de119_TotalCashAmt = ReadFixPacked(buf, ref offset, 16);
    if ((de1_SecondaryBitmap[6] &#038; 1) &gt; 0) de120_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 128 ) &gt; 0) de121_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 64) &gt; 0) de122_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 32) &gt; 0) de123_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 16) &gt; 0) de124_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 8 ) &gt; 0) de125_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 4) &gt; 0) de126_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 2) &gt; 0) de127_ResvPriv = ReadVar3Raw(buf, ref offset, 999);
    if ((de1_SecondaryBitmap[7] &#038; 1) &gt; 0) de128_MAC = ReadFixRaw(buf, ref offset, 8 );
  }
  #endregion

  /// &lt;summary&gt;
  /// Format the fields as a nice string using reflection
  /// &lt;/summary&gt;
  public override string ToString()
  {
    StringBuilder sb = new StringBuilder();
    foreach (FieldInfo i in this.GetType().GetFields())
    {
      object val = i.GetValue(this);
      if (val != null)
      {
        if (val is byte[])
          sb.AppendFormat("{0}: {1}\r\n", i.Name, BitConverter.ToString((byte[])val));
        else
          sb.AppendFormat("{0}: {1}\r\n", i.Name, val);
      }
    }
    return sb.ToString();
  }

  #region Internal helpers
  /// &lt;summary&gt;
  /// Converts 0-15 to '0-9A-F'
  /// &lt;/summary&gt;
  char XtoC(int x)
  {
    if (x &lt; 10) return (char)('0' + x);
    return (char)('A' + x);
  }

  /// &lt;summary&gt;
  /// Grab bytes from a buffer
  /// &lt;/summary&gt;
  byte[] B(byte[] buf, int off, int len)
  {
    byte[] a = new byte[len];
    Array.Copy(buf, off, a, 0, len);
    return a;
  }

  /// &lt;summary&gt;
  /// Converts a buffer to a string
  /// &lt;/summary&gt;
  string S(byte[] buf)
  {
    return Encoding.ASCII.GetString(buf);
  }

  byte[] ReadFixRaw(byte[] buf, ref int offset, int len)
  {
    byte[] val = B(buf, offset, len);
    offset += len;
    return val;
  }

  byte[] ReadVar2Raw(byte[] buf, ref int offset, int unused)
  {
    int len = (buf[offset] - 0x30) * 10 + buf[offset + 1] - 0x30;
    int oldoffset = offset;
    offset += len + 2;
    return B(buf, oldoffset + 2, len);
  }

  byte[] ReadVar3Raw(byte[] buf, ref int offset, int unused)
  {
    int len = (buf[offset]-0x30)*100 + (buf[offset+1]-0x30)*10 + buf[offset+2]-0x30;
    int oldoffset = offset;
    offset += len + 3;
    return B(buf, oldoffset + 3, len);
  }

  /// &lt;summary&gt;
  /// Reads an LLVARn
  /// eg input: 31 31 12 34 56 78 91 2F
  /// 31 31 is ascii '11' which means 11 numbers
  /// Those 11 numbers are binary packed decimal = 1234567891
  /// The 'F' at the end is filler.
  /// &lt;/summary&gt;
  string ReadVar2Packed(byte[] buf, ref int offset, int unused)
  {
    int len = (buf[offset] - 0x30) * 10 + (buf[offset + 1] - 0x30);
    int bytes = (len + 1) / 2; // The +1 is so it rounds up
    byte b;
    StringBuilder sb = new StringBuilder(len);
    if (len % 2 == 0) // Even number of chars, so there's no padding at the end
    {
      for (int i = 0; i &lt; bytes; i++)
      {
        b = buf[offset + 2 + i];
        sb.Append(XtoC(b &gt;&gt; 4)).Append(XtoC(b &#038; 0xf));
      }
    }
    else
    {
      int i;
      for (i = 0; i &lt; bytes - 1; i++)
      {
        b = buf[offset + 2 + i];
        sb.Append(XtoC(b &gt;&gt; 4)).Append(XtoC(b &#038; 0xf));
      }
      sb.Append(XtoC(buf[offset + 2 + i] &gt;&gt; 4)); // Get the last char
    }
    offset += bytes + 2;
    return sb.ToString();
  }

  /// &lt;summary&gt;
  /// Reads an LLLVARn
  /// eg input: 30 31 31 12 34 56 78 91 2F
  /// 30 31 31 is ascii '011' which means 11 numbers
  /// Those 11 numbers are binary packed decimal = 1234567891
  /// The 'F' at the end is filler.
  /// &lt;/summary&gt;
  string ReadVar3Packed(byte[] buf, ref int offset, int unused)
  {
    int len = (buf[offset] - 0x30) * 100 + (buf[offset + 1] - 0x30) * 10 + (buf[offset + 2] - 0x30);
    int bytes = (len + 1) / 2; // The +1 is so it rounds up
    byte b;
    StringBuilder sb = new StringBuilder(len);
    if (len % 2 == 0) // Even number of chars, so there's no padding at the end
    {
      for (int i = 0; i &lt; bytes; i++)
      {
        b = buf[offset + 3 + i];
        sb.Append(XtoC(b &gt;&gt; 4)).Append(XtoC(b &#038; 0xf));
      }
    }
    else
    {
      int i;
      for (i = 0; i &lt; bytes - 1; i++)
      {
        b = buf[offset + 3 + i];
        sb.Append(XtoC(b &gt;&gt; 4)).Append(XtoC(b &#038; 0xf));
      }
      sb.Append(XtoC(buf[offset + 3 + i] &gt;&gt; 4)); // Get the last char
    }
    offset += bytes + 3;
    return sb.ToString();
  }

  /// &lt;summary&gt;
  /// Parse a fixed length packed [hexi]decimal field
  /// If it is an odd number of digits, it skips the first nibble
  /// eg for input 0x01 23 of length 3, it grabs the "123"
  /// &lt;/summary&gt;
  string ReadFixPacked(byte[] buf, ref int offset, int len)
  {
    int bytes = (len + 1) / 2; // The +1 is so it rounds up
    byte b;
    StringBuilder sb = new StringBuilder(len);
    if (len % 2 == 0) // Even number of chars, so there's no padding at the end
    {
      for (int i = 0; i &lt; bytes; i++)
      {
        b = buf[offset + i];
        sb.Append(XtoC(b &gt;&gt; 4)).Append(XtoC(b &#038; 0xf));
      }
    }
    else // Odd number
    {
      int i;
      sb.Append(XtoC(buf[offset] &#038; 0xf)); // Get the first char from the second nibble
      for (i = 1; i &lt; bytes; i++)
      {
        b = buf[offset + i];
        sb.Append(XtoC(b &gt;&gt; 4)).Append(XtoC(b &#038; 0xf));
      }
    }
    offset += bytes;
    return sb.ToString();
  }
  #endregion
}
</pre>
<p>And here&#039;s example code to use it:</p>
<pre>
using System;

class Program
{
  static void Main(string[] args)
  {
    // Raw bytes for an AS2805 message
    byte[] message = new byte[] {
      0x02, 0x00,
      0x80, 0x38, 0x00, 0x01, 0x02, 0xC1, 0x00, 0x00,
      0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x65, 0x43, 0x21,
      0x13, 0x58, 0x40,
      0x04, 0x19,
      0x31, 0x31, 0x00, 0x08, 0x76, 0x54, 0x32, 0x1F,
      0x30, 0x30,
      0x38, 0x34, 0x30, 0x37, 0x31, 0x32, 0x33, 0x34,
      0x37, 0x37, 0x37, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x32, 0x31, 0x32, 0x33, 0x32, 0x31,
      0x30, 0x30, 0x38,
      0x8C, 0xA0, 0xA6, 0x42, 0x0C, 0x5C, 0xA6, 0x28,
      0x01, 0x23};

    // Parse the bytes into an instance of the message class
    As2805Message m = new As2805Message(message);
    // Display the parsed fields
    Console.WriteLine(m.ToString());

    Console.WriteLine("Press a key");
    Console.ReadKey();
  }
}
</pre>
<p>&#8230;and if anyone&#039;s wondering &#039;what is as2805&#039;, you probably use it every day: it&#039;s the message format standard used by EFTPOS.</p>
<style>pre {line-height:1em;width:100%;overflow:auto;border:1px solid silver;background:#ddf}</style>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=256</wfw:commentRss>
		</item>
		<item>
		<title>Ruby hex dumper</title>
		<link>http://splinter.com.au/blog/?p=254</link>
		<comments>http://splinter.com.au/blog/?p=254#comments</comments>
		<pubDate>Tue, 04 May 2010 05:54:18 +0000</pubDate>
		<dc:creator>Chris Hulbert</dc:creator>
		
		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://splinter.com.au/blog/?p=254</guid>
		<description><![CDATA[A quick little script for dumping a binary file into a view similar to a hex editor.Why not just *use* a hex editor, you say? Well, this allows me to carve up and document a binary file in word (or notepad+) as i try to parse its format from some limited documentation.
Anyway, here it is:

bytes [...]]]></description>
			<content:encoded><![CDATA[<p>A quick little script for dumping a binary file into a view similar to a hex editor.<span id="more-254"></span>Why not just *use* a hex editor, you say? Well, this allows me to carve up and document a binary file in word (or notepad+) as i try to parse its format from some limited documentation.<br />
Anyway, here it is:</p>
<p><code><br />
bytes = File.read("some-binary-file")<br />
while bytes<br />
  line = bytes[0,16]<br />
  bytes = bytes[16,bytes.length]</p>
<p>  d=&#034;<br />
  line.each_byte{|i| d+=&#034;%02X &#034;%i}<br />
  d+=&#034;   &#034;*(16-line.length)<br />
  d+=&#039;   &#039;<br />
  line.each_byte{|i| d+=(0&#215;20<=i &#038;&#038; i<=0x7E) ? i.chr : '.'}<br />
  puts d<br />
end<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://splinter.com.au/blog/?feed=rss2&amp;p=254</wfw:commentRss>
		</item>
	</channel>
</rss>
