Okay, for your amusement, a BCX/BSX viewer in Python:
import sys, struct, array, math, os, zlib
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL.ARB.texture_rectangle import *
from OpenGL.GL.ARB.multitexture import *
import pygame
from pygame.locals import *
bcxMap = {}
normals = []
def vecDot(a,b):
    return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]
def vecNormal(a):
    lenSqr = vecDot(a,a)
    if lenSqr > 0.0:
	ool = 1.0 / math.sqrt(lenSqr)
	return (a[0] * ool, a[1] * ool, a[2] * ool)
    else:
	return (0,0,0)
def loadLzs(name):
    # read compressed data
    f = open(name, "rb")
    buf = f.read()
    f.close()
    
    (size,) = struct.unpack_from("<I", buf, 0)
    if size + 4 != len(buf):
	del buf
	return None
    ibuf = array.array("B", buf)
    obuf = array.array("B")
    
    # decompress;
    iofs = 4
    cmd = 0
    bit = 0
    while iofs < len(ibuf):
	if bit == 0:
	    cmd = ibuf[iofs]
	    bit = 8
	    iofs += 1
	if cmd & 1:
	    obuf.append(ibuf[iofs])
	    iofs += 1
	else:
	    a = ibuf[iofs]
	    iofs += 1
	    b = ibuf[iofs]
	    iofs += 1
	    o = a | ((b & 0xF0) << 4)
	    l = (b & 0xF) + 3;
	    rofs = len(obuf) - ((len(obuf) - 18 - o) & 0xFFF)
	    for j in xrange(l):
		if rofs < 0:
		    obuf.append(0)
		else:
		    obuf.append(obuf[rofs])
		rofs += 1
	cmd >>= 1
	bit -= 1
    return obuf.tostring()
def loadFieldBin(name):
    # read compressed data
    f = open(name, "rb")
    buf = f.read()
    f.close()
    
    (size,) = struct.unpack_from("<I", buf, 0)
    # decompress
    data = zlib.decompress(buf[8:], 16 | 15)
    if len(data) != size:
	del buf
	del data
	return None
    return data
   
def readModel(data, num_bones, offset_skeleton, num_parts, offset_parts):
    model = {}
    
    skeleton = []
    for i in xrange(num_bones):
	(length, parent, has_mesh) = struct.unpack_from("<hbB", data, offset_skeleton + i * 4)
	skeleton.append((length, parent, has_mesh != 0))
    model["skeleton"] = skeleton
    
    parts = []
    for i in xrange(num_parts):
	part = {}
	(u1, bone_index, num_vertex, num_texcoord, num_quad_color_tex, num_tri_color_tex, num_quad_mono_tex, num_tri_mono_tex, num_tri_mono, num_quad_mono, num_tri_color, num_quad_color, u3, offset_poly, offset_texcoord, offset_flags, offset_control, u4, offset_vertex) = struct.unpack_from("<BBBBBBBBBBBBHHHHHHH", data, offset_parts + i * 0x20)
	# print (u1, bone_index, num_vertex, num_texcoord, num_quad_color_tex, num_tri_color_tex, num_quad_mono_tex, num_tri_mono_tex, num_tri_mono, num_quad_mono, num_tri_color, num_quad_color, u3, offset_poly, offset_texcoord, offset_flags, offset_control, u4, offset_vertex)
	#print "offset_vertex %x %x %x" % (offset_vertex, num_vertex, offset_vertex + 4 + num_vertex * 8)
	#print "offset_texcoord %x %x %x" % (offset_vertex + offset_texcoord, num_texcoord, offset_vertex + offset_texcoord + num_texcoord * 2)
	part["bone_index"] = bone_index
	# print (u1, u3, u4)
	
	vertices = []
	for j in xrange(num_vertex):
	    (x, y, z) = struct.unpack_from("<hhh", data, offset_vertex + 4 + j * 8)
	    vertices.append((x,y,z))
	part["vertices"] = vertices
		
	texcoords = []
	for j in xrange(num_texcoord):
	    (u, v) = struct.unpack_from("<BB", data, offset_vertex + offset_texcoord + j * 2)
	    texcoords.append((u,v))
	part["texcoords"] = texcoords
	
	cur_poly = offset_vertex + offset_poly
	cur_flags = offset_vertex + offset_flags
	cur_control = offset_vertex + offset_control
	# print "u4 : %4.4x" % (offset_vertex + u4)
	# print "cur_poly : %4.4x" % (cur_poly)
	# print "cur_flags : %4.4x" % (cur_flags)
	# print "cur_control : %4.4x" % (cur_control)
	quad_color_tex = []
	for j in xrange(num_quad_color_tex):
	    control = struct.unpack_from("<B", data, cur_control)
	    cur_control += 1
	
	    (av, bv, cv, dv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn, dr, dg, db, dn, at, bt, ct, dt) = struct.unpack_from("<BBBBBBBBBBBBBBBBBBBBBBBB", data, cur_poly)
	    poly = ((av, at, (ar, ag, ab), an), (bv, bt, (br, bg, bb), bn), (dv, dt, (dr, dg, db), dn), (cv, ct, (cr, cg, cb), cn))
	    # print "quad_color_tex %i %i %i %i / %i %i %i %i" % (av, bv, cv, dv, an, bn, cn, dn)
	    quad_color_tex.append(poly)
	    cur_poly += 0x18
	part["quad_color_tex"] = quad_color_tex
	tri_color_tex = []
	for j in xrange(num_tri_color_tex):
	    control = struct.unpack_from("<B", data, cur_control)
	    cur_control += 1
	
	    (av, bv, cv, xv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn, at, bt, ct, xt) = struct.unpack_from("<BBBBBBBBBBBBBBBBBBBB", data, cur_poly)
	    poly = ((av, at, (ar, ag, ab), an), (bv, bt, (br, bg, bb), bn), (cv, ct, (cr, cg, cb), cn), xv, xt)
	    # print "tri_color_tex %i %i %i / %x %x %x" % (av, bv, cv, an, bn, cn)
	    tri_color_tex.append(poly)
	    cur_poly += 0x14
	part["tri_color_tex"] = tri_color_tex
	quad_mono_tex = []
	for j in xrange(num_quad_mono_tex):
	    control = struct.unpack_from("<B", data, cur_control)
	    cur_control += 1
	
	    (av, bv, cv, dv, r, g, b, n, at, bt, ct, dt) = struct.unpack_from("<BBBBBBBBBBBB", data, cur_poly)
	    # print "quad_mono_tex", x
	    poly = ((av, at), (bv, bt), (dv, dt), (cv, ct), (r, g, b), n)
	    quad_mono_tex.append(poly)
	    cur_poly += 0x0C
	part["quad_mono_tex"] = quad_mono_tex
	tri_mono_tex = []
	for j in xrange(num_tri_mono_tex):
	    control = struct.unpack_from("<B", data, cur_control)
	    cur_control += 1
	
	    (av, bv, cv, xv, r, g, b, n, at, bt, ct, xt) = struct.unpack_from("<BBBBBBBBBBBB", data, cur_poly)
	    poly = ((av, at), (bv, bt), (cv, ct), (r, g, b), n, xv, xt)
	    # print "tri_mono_tex", x, xv, xt
	    tri_mono_tex.append(poly)
	    cur_poly += 0x0C
	part["tri_mono_tex"] = tri_mono_tex
	tri_mono = []
	for j in xrange(num_tri_mono):
	    (av, bv, cv, xv, r, g, b, n) = struct.unpack_from("<BBBBBBBB", data, cur_poly)
	    poly = ((av,), (bv,), (cv,), (r, g, b), n, xv)
	    # print "tri_mono", x, xv
	    tri_mono.append(poly)
	    cur_poly += 8
	part["tri_mono"] = tri_mono
	quad_mono = []
	for j in xrange(num_quad_mono):
	    (av, bv, cv, dv, r, g, b, n) = struct.unpack_from("<BBBBBBBB", data, cur_poly)
	    poly = ((av,), (bv,), (dv,), (cv,), (r, g, b), n)
	    # print "quad_mono", x
	    quad_mono.append(poly)
	    cur_poly += 8
	part["quad_mono"] = quad_mono
	tri_color = []
	for j in xrange(num_tri_color):
	    (av, bv, cv, xv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn) = struct.unpack_from("<BBBBBBBBBBBBBBBB", data, cur_poly)
	    poly = ((av,(ar, ag, ab), an), (bv, (br, bg, bb), bn), (cv, (cr, cg, cb), cn), xv)
	    # print "tri_color %i %i %i / %i %i %i %i / %i" % (av, bv, cv, an, bn, cn, xv, len(vertices))
	    tri_color.append(poly)
	    cur_poly += 0x10
	part["tri_color"] = tri_color
	quad_color = []
	for j in xrange(num_quad_color):
	    (av, bv, cv, dv, ar, ag, ab, an, br, bg, bb, bn, cr, cg, cb, cn, dr, dg, db, dn) = struct.unpack_from("<BBBBBBBBBBBBBBBBBBBB", data, cur_poly)
	    poly = ((av,(ar, ag, ab), an), (bv, (br, bg, bb), bn), (dv, (dr, dg, db), dn), (cv, (cr, cg, cb), cn))
	    # print "quad_color %i %i %i %i / %x %x %x %x" % (av, bv, cv, dv, an, bn, cn, dn)
	    quad_color.append(poly)
	    cur_poly += 0x14
	part["quad_color"] = quad_color
	
	# print "end_poly : %4.4x" % (cur_poly)
	# print "end_flags : %4.4x" % (cur_flags)
	# print "end_control : %4.4x" % (cur_control)
	
	parts.append(part)
		
    model["parts"] = parts
    	
    return model
    
def readAnimations(data, num_animations, offset_animations):
    animations = []
    for i in xrange(num_animations):
	(num_frames, num_channel, num_frames_translation, num_static_translation, num_frames_rotation, offset_frames_translation, offset_static_translation, offset_frames_rotation, offset_data) = struct.unpack_from("<HBBBBHHHI", data, offset_animations + i * 0x10)
	offset_data &= 0x7FFFFFFF
	# print "animation:", (offset_animations + i * 0x10, num_frames, num_channel, num_frames_translation, num_static_translation, num_frames_rotation, offset_frames_translation, offset_static_translation, offset_frames_rotation, offset_data)
	animation = []
	for k in xrange(num_channel):
	    (flag, rx, ry, rz, tx, ty, tz) = struct.unpack_from("<BBBBBBB", data, offset_data + 0x04 + k * 8)
	    channel = []
	    for j in xrange(num_frames):
                # rotation
                if flag & 0x01:
                    rotation_x = 360.0 * float(ord(data[offset_data + offset_frames_rotation + rx * num_frames + j])) / 255.0
                else:
                    rotation_x = 360.0 * rx / 255.0
                if flag & 0x02:
                    rotation_y = 360.0 * float(ord(data[offset_data + offset_frames_rotation + ry * num_frames + j])) / 255.0
                else:
                    rotation_y = 360.0 * ry / 255.0
                if flag & 0x04:
                    rotation_z = 360.0 * float(ord(data[offset_data + offset_frames_rotation + rz * num_frames + j])) / 255.0
                else:
                    rotation_z = 360.0 * rz / 255.0
                # translation
                if flag & 0x10:
                    (translation_x,) = struct.unpack_from("<h", data, offset_data + offset_frames_translation + tx * num_frames * 2 + j * 2)
                elif tx != 0xFF:
                    (translation_x,) = struct.unpack_from("<h", data, offset_data + offset_static_translation + tx * 2)
		else:
		    translation_x = 0
                if flag & 0x20:
                    (translation_y,) = struct.unpack_from("<h", data, offset_data + offset_frames_translation + ty * num_frames * 2 + j * 2);
                elif ty != 0xFF:
                    (translation_y,) = struct.unpack_from("<h", data, offset_data + offset_static_translation + ty * 2)
		else:
		    translation_y = 0
                if flag & 0x40:
                    (translation_z,) = struct.unpack_from("<h", data, offset_data + offset_frames_translation + tz * num_frames * 2 + j * 2)
                elif tz != 0xFF:
                    (translation_z,) = struct.unpack_from("<h", data, offset_data + offset_static_translation + tz * 2)
		else:
		    translation_z = 0
		channel.append(((translation_x, translation_y, translation_z), (rotation_x, rotation_y, rotation_z)))
	    animation.append(channel)
	animations.append(animation)
    return animations
def loadBcx(name):
    data = loadLzs(name)
    (size, header_offset) = struct.unpack_from("<II", data, 0)
    assert(size == len(data))
    (u1, num_bones, num_parts, num_animations, u2, u3, u4, offset_skeleton) = struct.unpack_from("<HBBB19sHHI", data, header_offset)
    offset_skeleton &= 0x7FFFFFFF
    offset_parts = offset_skeleton + num_bones * 4
    offset_animations = offset_parts + num_parts * 32
    # print "header", (header_offset, u1, num_bones, num_parts, num_animations, u2, u3, u4, offset_skeleton, offset_parts, offset_animations)
    object = {}
    object["model"] = readModel(data, num_bones, offset_skeleton, num_parts, offset_parts)
    object["animations"] = readAnimations(data, num_animations, offset_animations)
    
    return object
    
def loadBsx(name):
    # get compressed file
    data = loadLzs(name)
    (size, header_offset) = struct.unpack_from("<II", data, 0)
    assert(size == len(data))
    # decode individual objects
    object_list = []
    (u1, num_models) = struct.unpack_from("<II", data, header_offset)
    for i in xrange(num_models):
	model_offset = header_offset + 0x10 + i * 0x30
	(model_id,u2,u3,offset_skeleton,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15,u16,u17,u18,u19,u20,num_bones,u22,u23,u24,u25,u26,u27,u28,u29,u30,u31,u32,num_parts,u34,u35,u36,u37,u38,u39,u40,u41,u42,u43,u44,num_animations) = struct.unpack_from("<HBBHBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", data, model_offset)
	# print (model_id,u2,u3,offset_skeleton,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15,u16,u17,u18,u19,u20,num_bones,u22,u23,u24,u25,u26,u27,u28,u29,u30,u31,u32,num_parts,u34,u35,u36,u37,u38,u39,u40,u41,u42,u43,u44,num_animations)
	offset_skeleton += model_offset
	offset_parts = offset_skeleton + num_bones * 4
	offset_animations = offset_parts + num_parts * 32
	if model_id == 0x0100:
	    object = loadBcx(bcxMap["cloud"])
	elif model_id == 0x0302:
	    object = loadBcx(bcxMap["ballet"]) 
	elif model_id == 0x0403:
	    object = loadBcx(bcxMap["tifa"]) 
	elif model_id == 0x0605:
	    object = loadBcx(bcxMap["cid"])
	elif model_id == 0x0706:
	    object = loadBcx(bcxMap["yufi"])
	elif num_parts > 0:
	    object = {}
	    object["model"] = readModel(data, num_bones, offset_skeleton, num_parts, offset_parts)
	    object["animations"] = []
	else:
	    print "unknown model id: %x" % model_id
	
	object["animations"].extend(readAnimations(data, num_animations, offset_animations))
	
	object_list.append(object)
    return object_list
    
class OpenGLObject:
    def __init__(self, object):
	self.object = object
	self.time = 0
	self.animation = 0
	self.draw_normals = False
	self.draw_skeleton = True
	self.part_map = {}
	model = object["model"]
	for idx, part in enumerate(model["parts"]):
	    self.part_map[part["bone_index"]] = idx
    def nextAnim(self):
	self.animation = (self.animation + 1) % len(self.object["animations"])
	self.time = 0
	
    def drawPart(self, part):
	glEnable(GL_TEXTURE_2D)
	
	glBegin(GL_TRIANGLES)
	for tri in part["tri_color_tex"]:
	    for i in xrange(3):
		pos = part["vertices"][tri[i][0]]
		texcoord = part["texcoords"][tri[i][1]]
		col = tri[i][2]
		glTexCoord2i(texcoord[0], texcoord[1])
		glColor3ub(col[0], col[1], col[2])
		glVertex3i(pos[0], pos[1], pos[2])
    
	for tri in part["tri_mono_tex"]:
	    col = tri[3]
	    glColor3ub(col[0], col[1], col[2])
	    for i in xrange(3):
		pos = part["vertices"][tri[i][0]]
		texcoord = part["texcoords"][tri[i][1]]
		glTexCoord2i(texcoord[0], texcoord[1])
		glColor3ub(col[0], col[1], col[2])
		glVertex3i(pos[0], pos[1], pos[2])
	
	glEnd()
	
	glBegin(GL_QUADS)
	for quad in part["quad_color_tex"]:
	    for i in xrange(4):
		pos = part["vertices"][quad[i][0]]
		texcoord = part["texcoords"][quad[i][1]]
		col = quad[i][2]
		glTexCoord2i(texcoord[0], texcoord[1])
		glColor3ub(col[0], col[1], col[2])
		glVertex3i(pos[0], pos[1], pos[2])
	for quad in part["quad_mono_tex"]:
	    col = quad[4]
	    glColor3ub(col[0], col[1], col[2])
	    for i in xrange(4):
		pos = part["vertices"][quad[i][0]]
		texcoord = part["texcoords"][quad[i][1]]
		glTexCoord2i(texcoord[0], texcoord[1])
		glVertex3i(pos[0], pos[1], pos[2])
	
	glEnd()
	
	glDisable(GL_TEXTURE_2D)
	
	glBegin(GL_TRIANGLES)
	for tri in part["tri_color"]:
	    for i in xrange(3):
		pos = part["vertices"][tri[i][0]]
		col = tri[i][1]
		glColor3ub(col[0], col[1], col[2])
		glVertex3i(pos[0], pos[1], pos[2])
	for tri in part["tri_mono"]:
	    col = tri[3]
	    glColor3ub(col[0], col[1], col[2])
	    for i in xrange(3):
		pos = part["vertices"][tri[i][0]]
		glVertex3i(pos[0], pos[1], pos[2])
	glEnd()
	
	glBegin(GL_QUADS)
	for quad in part["quad_color"]:
	    for i in xrange(4):
		pos = part["vertices"][quad[i][0]]
		col = quad[i][1]
		glColor3ub(col[0], col[1], col[2])
		glVertex3i(pos[0], pos[1], pos[2])
	for quad in part["quad_mono"]:
	    col = quad[4]
	    glColor3ub(col[0], col[1], col[2])
	    for i in xrange(4):
		pos = part["vertices"][quad[i][0]]
		glVertex3i(pos[0], pos[1], pos[2])
	
	glEnd()
	
	# draw normals (I'm not sure about the mono primitives. Maybe they're not lit at all?)
	if self.draw_normals:
	    global normals
	    glBegin(GL_LINES)
	    glColor3f(1,1,0)
	    for quad in part["quad_mono_tex"]:
		for i in xrange(4):
		    pos = part["vertices"][quad[i][0]]
		    nrm = normals[quad[5]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for tri in part["tri_mono_tex"]:
		for i in xrange(3):
		    pos = part["vertices"][tri[i][0]]
		    nrm = normals[tri[4]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for quad in part["quad_mono"]:
		for i in xrange(4):
		    pos = part["vertices"][quad[i][0]]
		    nrm = normals[quad[5]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for tri in part["tri_mono"]:
		for i in xrange(3):
		    pos = part["vertices"][tri[i][0]]
		    nrm = normals[tri[4]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for quad in part["quad_color_tex"]:
		for i in xrange(4):
		    pos = part["vertices"][quad[i][0]]
		    nrm = normals[quad[i][3]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for tri in part["tri_color_tex"]:
		for i in xrange(3):
		    pos = part["vertices"][tri[i][0]]
		    nrm = normals[tri[i][3]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for quad in part["quad_color"]:
		for i in xrange(4):
		    pos = part["vertices"][quad[i][0]]
		    nrm = normals[quad[i][2]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    for tri in part["tri_color"]:
		for i in xrange(3):
		    pos = part["vertices"][tri[i][0]]
		    nrm = normals[tri[i][2]]
		    glVertex3i(pos[0], pos[1], pos[2])
		    glVertex3i(int(pos[0]+nrm[0]*50), int(pos[1]+nrm[1]*50), int(pos[2]+nrm[2]*50))
	    
	    glEnd()
	
    def draw(self):
	glMatrixMode(GL_MODELVIEW)
	glRotatef(180, 1, 0, 0)
	model = self.object["model"]
	animation = self.object["animations"][self.animation]
	skeleton = model["skeleton"]
	self.time = (self.time + 1) % len(animation[0])
	
	parent = [-1]
	for idx, bone in enumerate(model["skeleton"]):
	    ((translation_x, translation_y, translation_z), (rotation_x, rotation_y, rotation_z)) = animation[idx][self.time]
	    while parent[-1] != bone[1]:
		parent.pop()
		glPopMatrix()
	    parent.append(idx)
	    glPushMatrix()
	    if self.draw_skeleton:
		glBegin(GL_LINES)
		glColor3f(1.0, 0.0, 1.0)
		glVertex3i(0, 0, 0)
		glVertex3i(0, 0, bone[0])
		glColor3f(1.0, 0.0, 0.0)
		glVertex3i(0, 0, 0)
		glVertex3i(50, 0, 0)
		glColor3f(0.0, 1.0, 0.0)
		glVertex3i(0, 0, 0)
		glVertex3i(0, 50, 0)
		glColor3f(0.0, 0.0, 1.0)
		glVertex3i(0, 0, 0)
		glVertex3i(0, 0, 50)
		glEnd()
	    glTranslatef(0, 0, bone[0])	    
	    glRotatef(rotation_y, 0, 1, 0)
	    glRotatef(rotation_x, 1, 0, 0)
	    glRotatef(rotation_z, 0, 0, 1)
	    glTranslatef(translation_x, translation_y, translation_z)
	    if bone[2]:
		self.drawPart(model["parts"][self.part_map[idx]])
	while parent[-1] != -1:
	    parent.pop()
	    glPopMatrix()
	
    
def main(*argv):
    pygame.init()
    
    screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF|OPENGL)
    glViewport(0, 0, 640, 480)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    kFOVy = 0.57735
    kZNear = 1000.0
    kZFar = 3000.0
    aspect = (640.0 / 480.0) * kZNear * kFOVy
    glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)		
    glDisable(GL_CULL_FACE)
    glDisable(GL_LIGHTING)
    glDisable(GL_BLEND)
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
        
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
    filePath = os.path.abspath(argv[0])
    # get list of BCX, which should be in the same dir as the .bsx
    # (This is done this way to support operating sytems with a case-sensitive file systems.)
    global bcxMap
    bcxMap = {}
    fieldInfo = {}
    dir = os.path.dirname(filePath)
    for path in os.listdir(dir):
	file = os.path.splitext(path)
        if file[1].lower() == ".bcx":
	    bcxMap[file[0].lower()] = os.path.join(dir, path)
	elif path.lower() == "field.bin":
	    fieldInfo["field.bin"] = os.path.join(dir, path)
	elif path.lower() == "field.tdb":
	    fieldInfo["field.tdb"] = os.path.join(dir, path)
    # get normals
    fieldBin = loadFieldBin(fieldInfo["field.bin"])
    global normals
    normals = []
    for i in xrange(240):
	n = struct.unpack_from("<hhh", fieldBin, 0x3f520 + i * 8)
	normals.append(vecNormal(n))
    # load BCX or BSX file
    if os.path.splitext(os.path.basename(filePath))[1].lower() == ".bcx":
	data = [loadBcx(filePath)]
    else:
	data = loadBsx(filePath)
    clock = pygame.time.Clock()
    key_up = key_down = key_left = key_right = False
    rot_x = 0.0
    rot_y = 0.0
    cur_model = 0
    object = OpenGLObject(data[cur_model])
    wireframe = True
    while True:
	for event in pygame.event.get():
	    if event.type == QUIT:
		exit()
	    elif event.type == KEYDOWN:
		if event.key == pygame.K_UP:
		    key_up = True
		elif event.key == pygame.K_DOWN:
		    key_down = True
		elif event.key == pygame.K_LEFT:
		    key_left = True
		elif event.key == pygame.K_RIGHT:
		    key_right = True
		elif event.key == pygame.K_ESCAPE:
		    exit()
		elif event.key == pygame.K_a:
		    object.nextAnim()
		elif event.key == pygame.K_n:
		    object.draw_normals = not object.draw_normals
		elif event.key == pygame.K_s:
		    object.draw_skeleton = not object.draw_skeleton
		elif event.key == pygame.K_m:
		    cur_model = (cur_model + 1) % len(data)
		    del object
		    object = OpenGLObject(data[cur_model])
		elif event.key == pygame.K_w:
		    wireframe = not wireframe
		    if wireframe:
		        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
		    else:
		        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
	    elif event.type == KEYUP:
		if event.key == pygame.K_UP:
		    key_up = False
		elif event.key == pygame.K_DOWN:
		    key_down = False
		elif event.key == pygame.K_LEFT:
		    key_left = False
		elif event.key == pygame.K_RIGHT:
		    key_right = False
		    
	if key_up:
	    rot_x += 1.0
	elif key_down:
	    rot_x -= 1.0
	    
	if key_left:
	    rot_y += 1.0
	elif key_right:
	    rot_y -= 1.0
	    
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)	
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, 100000.0)
	glTranslatef(0, 0, -2000)
	glRotatef(rot_y * 360.0 / 256.0, 0.0, 1.0, 0.0)
	glRotatef(rot_x * 360.0 / 256.0, 1.0, 0.0, 0.0)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
	object.draw()
    
	clock.tick(60)
	pygame.display.flip()
if __name__ == "__main__":
    main(*sys.argv[1:])
You'll need pygame and the python-opengl bindings. Run with a BCX or BSX file as argument, w toggles wireframe, n toggles normals, s toggles the skeleton, m cycles through models and a cycles through animations. 
It should be quite easy to take it from here and make an importer for Blender, but if you're after efficient rendering or animation then Akari's solution in the q-gears source is more efficient.