home *** CD-ROM | disk | FTP | other *** search
- #
- # Street Fighter 4 EMO2SMD Converter
- # by magnum29
- #
-
- use Math::Trig;
-
- print "Enter an EMO file:\n";
- chomp( my $emo = <STDIN> );
- $emo =~ s/^"//;
- $emo =~ s/"$//;
- open $INPUT, "<$emo" or die "Couldn't open $emo: $!\n";
- binmode($INPUT);
-
- open O, ">${emo}_data.txt";
-
- seek $INPUT, 0x10, 0;
- my $skeleton_address = readu( $INPUT, "I", 4 );
-
- seek $INPUT, $skeleton_address, 0;
-
- my $total_nodes = readu( $INPUT, "I", 4 );
-
- seek $INPUT, 4, 1; # unknown, small number
-
- my $skeleton_start_offset = readu( $INPUT, "I", 4 );
- my $skeleton_name_addresses_offset = readu( $INPUT, "I", 4 );
-
- seek $INPUT, 8, 1; # 0 0
-
- seek $INPUT, 4, 1; # address for bunch of FF's
-
- my $matrix_offset = readu( $INPUT, "I", 4 );
-
- seek $INPUT, $skeleton_address + $skeleton_start_offset, 0;
-
- open SMD, ">${emo}.smd";
- print SMD "version 1\nnodes\n";
-
- my @node_parent;
- my @node_number;
- my @node_matrix;
- for( 0..$total_nodes-1 ) {
- my $parent = readu( $INPUT, "s", 2 );
- my $node_number = readu( $INPUT, "s", 2 );
-
- seek $INPUT, 12, 1; #skip unknowns (short + FF + FF + zeros)
-
- my @transformation_matrix = readu( $INPUT, "f16", 64 );
-
- push( @node_parent, $parent );
- push( @node_number, $node_number );
- push( @node_matrix, \@transformation_matrix );
-
- }
- print O "\n";
-
- seek $INPUT, $skeleton_address + $skeleton_name_addresses_offset, 0;
- my @skeleton_name_addresses = readu( $INPUT, "I$total_nodes", 4 * $total_nodes );
-
- for( 0..$total_nodes-1 ) {
- seek $INPUT, $skeleton_address + $skeleton_name_addresses[$_], 0;
-
- my $node_name = "";
- my $char;
- read( $INPUT, $char, 1 );
- while( $char ne "\0" ) {
- $node_name .= $char;
- read( $INPUT, $char, 1 );
- }
- print SMD "$_ \"$node_name\" $node_parent[$_]\n";
- printf O "%32s %3d - %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f\n", $node_name, $node_parent[$_], @{$node_matrix[$_]};
- }
- print O "\n";
-
- seek $INPUT, $skeleton_address + $matrix_offset, 0;
- print O "\n\nMATRIX DATA\n";
- for( 0..$total_nodes-1 ) {
- my @matrix = readu( $INPUT, "f16", 64 );
- printf O "%3d - %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f %9f\n", $_, @matrix;
- }
-
-
- print SMD "end\nskeleton\ntime 0\n";
-
- # [m00 m01 m02]
- # [m10 m11 m12]
- # [m20 m21 m22]
- # heading = Math.atan2(-m.m20,m.m00);
- # bank = Math.atan2(-m.m12,m.m11);
- # attitude = Math.asin(m.m10);
-
- for( 0..$total_nodes-1 ) {
- my @translation = (-${$node_matrix[$_]}[12],${$node_matrix[$_]}[14],${$node_matrix[$_]}[13]);
-
- my $ry = atan2( -${$node_matrix[$_]}[2], ${$node_matrix[$_]}[0] );
- my $rx = atan2( -${$node_matrix[$_]}[9], ${$node_matrix[$_]}[5] );
- my $rz = asin_real( ${$node_matrix[$_]}[1] );
-
- printf SMD "%d %f %f %f %f %f %f\n", $_, @translation, -$rx, $rz, $ry;
- }
-
- print SMD "end\ntriangles\n";
-
- seek $INPUT, 0x20, 0;
- my $num_emg_subfiles = readu( $INPUT, "S", 2 );
-
- seek $INPUT, 0x28, 0;
- my @emg_file_addresses = readu( $INPUT, "I$num_emg_subfiles", 4 * $num_emg_subfiles );
-
-
- $emo =~ s/^(.+?)([^\\\/]+)$/\2/;
- $emo =~ /^(......)/;
- my $texture_prefix = $1 . "_";
-
- for my $emg_address_i ( 0..$#emg_file_addresses ) {
- print O "EMG #$emg_address_i\n";
- print "\nEMG $emg_address_i/$#emg_file_addresses\n";
- my $emg_address = $emg_file_addresses[$emg_address_i];
- $emg_address += 0x20;
- seek $INPUT, $emg_address, 0;
- warn "Thing != 1" if readu( $INPUT, "I", 4 ) != 1; # 1
- warn "Thing != 0x10" if readu( $INPUT, "I", 4 ) != 0x10; # 0x10
- $emg_address += 0x10;
- seek $INPUT, $emg_address + 6, 0;
- warn "Thing2 != 1" if readu( $INPUT, "S", 2 ) != 1; # 1
- warn "Thing2 != 0x10" if readu( $INPUT, "I", 4 ) != 0x10; # 0x10
- $emg_address += 0x10;
-
- seek $INPUT, $emg_address, 0;
-
- readu( $INPUT, "I", 4 ); # 711
- my $a_blocks = readu( $INPUT, "I", 4 ); # number of subblocks in BLOCK A
- readu( $INPUT, "I", 4 ); # 0
- my $a_block_address = readu( $INPUT, "I", 4 ); # BLOCK A address
-
- my $vertexblockcount = readu( $INPUT, "S", 2 );
- my $vertexblocksize = readu( $INPUT, "S", 2 );
- my $vertexblockaddress = readu( $INPUT, "I", 4 );
-
- my $b_blocks = readu( $INPUT, "S", 2 ); # should be 1, or else warn
- my $b_subblocks = readu( $INPUT, "S", 2 );
- my $b_block_address = readu( $INPUT, "S", 2 );
-
- seek $INPUT, $emg_address + 0x20, 0;
- my @mesh_matrix = readu( $INPUT, "f12", 48 );
- printf O "%10f %10f %10f %10f\n%10f %10f %10f %10f\n%10f %10f %10f %10f\n\n", @mesh_matrix;
-
- seek $INPUT, $emg_address + $a_block_address, 0;
- my @a_block_addresses = readu( $INPUT, "I$a_blocks", 4 * $a_blocks );
- my @texture_block;
- for( 0..$a_blocks-1 ) {
- seek $INPUT, $emg_address + $a_block_addresses[$_], 0;
- my $num_textures = readu( $INPUT, "I", 4 );
-
- my $texture_num = readu( $INPUT, "n", 2 ); # Big Endian short
- push( @texture_block, $texture_num );
- }
-
- my @mesh_faces;
- my @mesh_name;
- my @mesh_nodes;
- my @mesh_ids;
- my @mesh_vector;
- seek $INPUT, $emg_address + $b_block_address, 0;
- my @b_subblock_addresses = readu( $INPUT, "I$b_subblocks", 4 * $b_subblocks );
- print "Face addresses - @b_subblock_addresses\n";
- for my $b_subblock_address( @b_subblock_addresses ) {
- seek $INPUT, $emg_address + $b_subblock_address, 0;
-
- my @floats = readu( $INPUT, "f4", 16 );
- push( @mesh_vector, \@floats );
-
- my $mesh_id = readu( $INPUT, "S", 2 );
- my $num_faces = readu( $INPUT, "S", 2 );
- my $num_shorts = readu( $INPUT, "S", 2 );
- my $mesh_name = readu( $INPUT, "a32", 32 );
- $mesh_name =~ s/\0+$//;
-
- push( @mesh_name, $mesh_name );
- push( @mesh_ids, $mesh_id );
-
- print O "[$mesh_id] - $mesh_name : $num_faces Face Indices, $num_shorts Bones\n@floats\n\n";
-
- my @allfaces = readu( $INPUT, "S$num_faces", 2 * $num_faces );
- print O "@allfaces\n\n";
-
- my @triangle_sets;
- my $backface = 1;
- for( 0..$#allfaces-2 ) {
- my ($a, $b, $c) = ($allfaces[$_],$allfaces[$_+1],$allfaces[$_+2]);
-
- $backface = ($backface + 1) % 2;
-
- if( $a != $b && $a != $c && $b != $c ) {
- my @faceset;
- if( $backface ) {
- @faceset = ($c, $b, $a);
- } else {
- @faceset = ($a, $b, $c);
- }
- push( @triangle_sets, \@faceset );
- }
- }
-
- push( @mesh_faces, \@triangle_sets );
-
- my @allshorts = readu( $INPUT, "S$num_shorts", 2 * $num_shorts );
-
- push( @mesh_nodes, \@allshorts );
- print O "@allshorts\n\n";
- print O "----------------------------------------------------------\n";
- }
-
- seek $INPUT, $emg_address + $vertexblockaddress, 0;
- print O "Vertices : $vertexblockcount\n";
- print O " x y z nx ny nz tu tv unknown1 unknown2 unknown3 unknowns bi0 bi1 bi2 bi3 w0 w1 w2\n";
-
- my @xyz;
- my @normal;
- my @uv;
- my @nodes;
- my @node_weights;
- for( 0..$vertexblockcount-1 ) {
-
- my @floats;
- if( $vertexblocksize == 64 ) {
- @floats = readu( $INPUT, "f11", 44 );
-
- my @v_uc = ($floats[6],-$floats[7]);
-
- my @v_xyz = (-$floats[0],$floats[2],$floats[1]);
- my @v_normal = (-$floats[3],$floats[5],$floats[4]);
-
- push( @uv, \@v_uc );
- push( @xyz, \@v_xyz );
- push( @normal, \@v_normal );
-
- } elsif( $vertexblocksize == 32 ) { # used in skeleton EMO files
- @floats = readu( $INPUT, "f3", 12 );
-
- my @v_xyz = ($floats[0],-$floats[2],$floats[1]);
- my @v_uc = (0,0);
- my @v_normal = (0,0,0);
-
- push( @uv, \@v_uc );
- push( @xyz, \@v_xyz );
- push( @normal, \@v_normal );
-
- } elsif( $vertexblocksize == 40 ) { # used in shadow EMO files
- @floats = readu( $INPUT, "f6", 24 );
-
- my @v_xyz = ($floats[0],-$floats[2],$floats[1]);
- my @v_uc = (0,0);
- my @v_normal = ($floats[3],-$floats[5],$floats[4]);
-
- push( @uv, \@v_uc );
- push( @xyz, \@v_xyz );
- push( @normal, \@v_normal );
- }
- printf O "%9f "x($#floats+1), @floats;
-
- if( $vertexblocksize != 40 ) {
- my @unknownchars = readu( $INPUT, "C4", 4 );
- printf O "| %3d %3d %3d %3d | ", @unknownchars;
- }
-
- my @chars = readu( $INPUT, "C4", 4 );
- printf O "%3d %3d %3d %3d | ", @chars;
-
- my @weight_floats = readu( $INPUT, "f3", 12 );
- my $remaining_weight = 0;
- my $total_weight = 0;
- $total_weight += $_ for @weight_floats;
- if( (1-$total_weight) > 0.001 ) {
- $remaining_weight = 1-$total_weight;
- }
- push( @weight_floats, $remaining_weight );
- printf O "%9f %9f %9f\n", @weight_floats;
-
- my %bone;
- my @revised_nodes;
- my @revised_weights;
- for( 0..3 ) {
- if( defined($bone{$chars[$_]}) ) {
- if( $weight_floats[$_] != 0 ) {
- print "Double Weight!\n";
- $revised_weights[$bone{$chars[$_]}] += $weight_floats[$_];
- }
- } else {
- if( $weight_floats[$_] > 0 ) {
- push( @revised_nodes, $chars[$_] );
- push( @revised_weights, $weight_floats[$_] );
- $bone{$chars[$_]} = $#revised_nodes;
- } elsif( $weight_floats[$_] < 0 ) {
- print "NEGATIVE WEIGHT: $weight_floats[$_]\n";
- }
-
- }
- }
- push( @nodes, \@revised_nodes );
- push( @node_weights, \@revised_weights );
- }
-
- for my $m( 0..$#mesh_faces ) {
-
- my @triangle_sets = @{$mesh_faces[$m]};
-
- print "Dumping mesh $m... ", $#triangle_sets+1, " triangle sets\n";
-
- for my $f( 0..$#triangle_sets ) {
- my @face_vertices = @{$triangle_sets[$f]};
-
- print SMD "$mesh_name[$m]\\..\\$texture_prefix$texture_block[$mesh_ids[$m]].dds\n";
- printf SMD "0 %10f %10f %10f %10f %10f %10f %10f %10f ", @{$xyz[$face_vertices[0]]}, @{$normal[$face_vertices[0]]}, @{$uv[$face_vertices[0]]};
- print SMD ($#{$nodes[$face_vertices[0]]}+1);
- for( 0..$#{$nodes[$face_vertices[0]]} ) {
- printf SMD " %3d %10f", $mesh_nodes[$m][${$nodes[$face_vertices[0]]}[$_]], ${$node_weights[$face_vertices[0]]}[$_];
- }
- print SMD "\n";
-
- printf SMD "0 %10f %10f %10f %10f %10f %10f %10f %10f ", @{$xyz[$face_vertices[1]]}, @{$normal[$face_vertices[1]]}, @{$uv[$face_vertices[1]]};
- print SMD ($#{$nodes[$face_vertices[1]]}+1);
- for( 0..$#{$nodes[$face_vertices[1]]} ) {
- printf SMD " %3d %10f", $mesh_nodes[$m][${$nodes[$face_vertices[1]]}[$_]], ${$node_weights[$face_vertices[1]]}[$_];
- }
- print SMD "\n";
-
- printf SMD "0 %10f %10f %10f %10f %10f %10f %10f %10f ", @{$xyz[$face_vertices[2]]}, @{$normal[$face_vertices[2]]}, @{$uv[$face_vertices[2]]};
- print SMD ($#{$nodes[$face_vertices[2]]}+1);
- for( 0..$#{$nodes[$face_vertices[2]]} ) {
- printf SMD " %3d %10f", $mesh_nodes[$m][${$nodes[$face_vertices[2]]}[$_]], ${$node_weights[$face_vertices[2]]}[$_];
- }
- print SMD "\n";
- }
- }
-
- print O "\n\nEMG FILE ENDED\n\n";
- }
-
- print SMD "end\n";
- print "\nDone.\n\n";
- system("pause");
-
-
-
-
- sub readu {
- my $temp;
- read( $_[0], $temp, $_[2] );
- return unpack( $_[1], $temp );
- }
-
-