use warnings; use strict; use feature 'say'; use File::Copy; if(scalar(@ARGV) == 0){ die 'we need some sort of input to what needs to be changed.'; } my %changes = (); for my $i(0 .. $#ARGV){ my @changes = $ARGV[$i] =~ m#^@([0-9]+)make([0-1]{8})$#; if(scalar(@changes) != 2){ die 'invalid input'; } $changes{$changes[0]} = $changes[1]; say 'making changes (', $changes[1], ') to address ', $changes[0]; } if(!exists($ENV{DIABLO_II_CHAR})){ die('DIABLO_II_CHAR global variable does not exist.'); } my $D2sDataFile = $ENV{DIABLO_II_CHAR}; #'C:\Users\user1\AppData\Local\VirtualStore\Program Files (x86)\Diablo II\Save\my_d.d2s'; my $D2sRevertFile = 'revert.d2s'; if(! -f $D2sDataFile){ die "$D2sDataFile does not exist! What do you want to change?"; } if(-f $D2sRevertFile){ say 'Revert file already exists, retaining..' } else{ say 'Saving d2s file to ', $D2sRevertFile; copy($D2sDataFile, $D2sRevertFile); } my $all; { local $/; open(my ${f}, '<', $D2sDataFile) or die $!; $all = <$f>; close(${f}); } #say 'in file checksum: ', +(unpack('H8', &get_actual_checksum()))[0]; #say 'my checksum: ', +(unpack('H8', &get_your_checksum()))[0]; say 'in file checksum: ', uc(&get_actual_checksum()); say 'my checksum: ', sprintf('%08X', &get_your_checksum()); my @all_chars = unpack('C*', $all); while(my ($loc,$value) = each(%changes)){ say "Location $loc changing to $value from ", sprintf('%08b', $all_chars[$loc]); $all_chars[$loc] = &bin($value); } $all = pack('C*', @all_chars); my $chksum = &get_your_checksum(); say 'new checksum: ', sprintf('%08X', $chksum); @all_chars = unpack('C*', $all); @all_chars[12 .. 15] = ( ($chksum & 0xff00_0000) >> 24, ($chksum & 0xff0000) >> 16, ($chksum & 0xff00) >> 8, $chksum & 0xff ); $all = pack('C*', @all_chars); { open(my ${f}, '>', $D2sDataFile) or die $!; syswrite(${f}, $all) or die $!; close(${f}); } sub get_actual_checksum(){ #say +(unpack('H32', $all))[0]; #return pack('H*', substr((unpack('H32', $all))[0], 24, 8)); return unpack('H*', substr($all, 12, 4)); } sub get_your_checksum(){ my $c = 0; my $addr = 0; my @cs = unpack('C*', $all); for my $ct(@cs){ $ct += $c >> 31 & 1; $c = (($addr >> 2 == 3) ? 0 : $ct) + ($c << 1); $addr++; } return ($c & 0xff000000) >> 24 | ($c & 0xff0000) >> 8 | ($c & 0xff00) << 8 | ($c & 0xff) << 24; } sub bin{ return oct('0b' . $_[0]); }