Posted 2002 or so

apple art bits c code evil games interaction iphone java js mathematica micro optimization php quine reveng sound updates useless video whine xterm zip
Feeds: RSS / Twitter

Remove ad (sets cookie)
PHP has finally taken the plunge into the 1960s by implementing GOTO. Here's an implementation for Java.
A patch for a serious Java bug. No longer needed as of June 16.
Max & Michael have written a Max/MSP driver based on the multitouch code.
This is a little test applet to shows the touch points as reported by the MacBook multitouch pad. The drivers seems to be able to track an impressive 11 fingers at once.

My CSS-fu is weak; please use a recent browser.

Some rights reserved.

Random, semi-related image by AmUnivers.

Horrible Java Tricks of Yore

This describes a horrible way of implementing createImage() on JDK1.0.2.

Due to a series of trip-ups on my part, my university web account recently started giving directory listings. As a result, Google got hold of several things that weren't supposed to be public. Nothing embarassing or illegal, just a lot of half-baked crap. Anyway, I decided to go through the files, and I stumbled upon a horrible bit of Java hackery I'd almost forgotten. I couldn't let such ugliness go to waste, so here it is :-)

Background

Back in 1998, an Australian company had decided to fix a perceived problem on the early web, namely this:

Their solution was to embed the image in an applet, thus making it harder to copy it.

Now, there is an obvious flaw with this solution, and anything like it (hint: how did I make the illustration above?). Let's disregard that for the moment. Pretend you actually wanted to write such an image-wrapping applet.

The compatibility thing

What they did was this: Their applet would load an image in their "secret" format (basically XORed with a magic number), decode it, and show it on screen.

However, their applet didn't work with Java 1.0.2-based browsers, since the method createImage(byte[] data) (which reads an image file and gives you an Image object) is 1.1 only.

My job was to make a replacement from whatever scraps and pieces could be found in 1.0.2. It's good exercise; if you're insane you might want to try before reading on.

The obvious place to look is java.awt.Toolkit (after all, that's where the API was added later). The only vaguely usable call there is createImage(ImageProducer foo).

Obviously you need to make an ImageProducer. There are only two of these: OffScreenImageSource and InputStreamImageSource. Sounds like we should be able to make a ByteArrayInputStream and pass that to ISIS? No. ISIS is abstract and has package access, so we can't even subclass it. Even the 1.0.2 verifier would notice if you tried to add classes named sun.something.

There is a JPEGImageDecoder class, but to use it you need to have an ISIS already.

FileImageSource won't help, as we can't write files. URLImageSource could help... I thought about implementing a very trivial HTTP server (probably 30 lines or so of code, still a lot less work than writing an image decoder). As an applet we would be allowed to start a web server, but we wouldn't be able to connect to it.

Hey, what's this

Fortunately, the JDK implementors were lazy. Look at this code:

    public URLImageSource(URL url1) {
        SecurityManager securitymanager = System.getSecurityManager();
        if(securitymanager != null)
            securitymanager.checkConnect(url1.getHost(), url1.getPort());
        url = url1;
    }

public URLImageSource(URLConnection urlconnection) { this(urlconnection.getURL(), urlconnection); }

I hope you can see where this is going. If not, stare at it some more :)

The URLImageSource can be created in two ways: You can ask for an image from a specified url (in which case I presume the first constructor is used), or you can open an URL, and ask for the object returned by that URL. When that happens, the URLConnection will already exist, which is why the second constructor is there. However, due to laziness, the constructor is public.

The hack

You can create your own fake URLConnection that pretends to be from some URL that the applet is allowed to visit.

Here's the final hack:

/***************************************************************************************

Minimal 1.0.2-safe image loader.

To use, replace

   Image i = getToolkit().createImage(data);

with

   Image i = ImgLoader.load(data,mainApplet);

To avoid wasting space and download time, move the code between the snip marks into
the main applet. After you have obfuscated the applet and made a jar file, you can 
remove the class files corresponding to ImgLoader from the jar - it will then be 
downloaded on demand by 1.02 browsers only (which generally won't support compressed 
JAR files anyway)

**************************************************************************************/

import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.applet.*;

public class ImgLoader extends URLConnection {  

//-- snip snip -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  
  static boolean sane=true;
  public static Image decode(byte[] data, Applet home) {  
    if(sane) {
      try {
        return saneLoader(data);       
        // if the createImage() method does not exist, the call to saneLoader will
        // fail, not the actual call to createImage().
      } catch (LinkageError e) {
         sane=false; // no need to try more than once
      }
    }
    return strangeLoader(data,home);
  }
  
  // dummy method to encapsulate the unsafe call, re note above
  // will throw a LinkageError on jdk1.02
  public static Image saneLoader(byte[] data) {
    return Toolkit.getDefaultToolkit().createImage(data);
  }

  // dummy method to avoid loading the AL class (if you move this code)
  public static Image strangeLoader(byte[]data, Applet home) {
    URL u = home.getDocumentBase();
    return Toolkit.getDefaultToolkit().
     createImage(new sun.awt.image.URLImageSource(u,new ImgLoader(data,u)));
  }
  
//-- snip snip -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    
  public void connect() {}
  
  // no need to worry that these mime types will get out of date - they are only used
  // on 1.02 browsers anyway
  public String getContentType() {
    switch(data[0]) {
      case (byte)'G':  return "image/gif";
      case (byte)0x89: return "image/x-png";
      default:         return "image/jpeg";
    }
  }
  
  public InputStream getInputStream() {
    return new ByteArrayInputStream(data,0,data.length);
  }
  public int getContentLength() {
    return data.length;
  }
  
  public ImgLoader(byte[] bob,URL u) {
    super(u);
    data=bob;
  }
  
  byte[] data;
}

Comments