#!/bin/bash # Comic Chat AVS format sprite sheet generator # by cidoku (https://cidoku.net) # Documentation and minor tweaks by ShadowM00n (https://shadowm00n.neocities.org) # # To the extent possible under law, the author(s) have dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty. # # You should have received a copy of the CC0 Public Domain Dedication along # with this software. If not, see # http://creativecommons.org/publicdomain/zero/1.0/ VERSION='v1.0.0' # Set up usage data usage() { printf "Usage: %s Options: -h, --help Show this help text and exit -v, --version Show version information and exit \n" "$(basename "$0")" } # Embedded manual show_manual() { man -l -- - <<'EOF' .TH "avs.sh" "1" "" "v1.0.0" "" .SH NAME avs.sh \- Batch\-create Microsoft Comic Chat Character Editor AVS files .SH SYNOPSIS \f[B]avs.sh\f[R] .SH DESCRIPTION The purpose of this script is to take a series of png images and a text file containing metadata and create a fully\-compliant AVS file for Microsoft Comic Chat Character Editor, using ImageMagick(1) to do the heavy lifting. This file can then be imported into the editor and used to create a fully\-working avatar in the form of an AVB file. .SH OPTIONS .TP \f[B]\-h\f[R], \f[B]\[en]help\f[R] Shows the help text and exits .TP \f[B]\-m\f[R], \f[B]\[en]man\f[R] Shows this manual and exits .TP \f[B]\-v\f[R], \f[B]\[en]version\f[R] Shows version information and exits .SH FILES For correct generation, a number of files are required in the project directory. .PP Fully mandatory: .IP \[bu] 2 info.txt (contains file metadata; the format is explained below) .IP \[bu] 2 icon.png (the \[lq]face\[rq] of the avatar in the user list) .PP Expected to some degree: .IP \[bu] 2 Any number of pose files (named a specific way; the format is explained below) .SH FILE STRUCTURES .SS info.txt The following data must be present, in this order, each on its own line: .IP \[bu] 2 Character name .IP \[bu] 2 Character description .IP \[bu] 2 Author .IP \[bu] 2 Copyright .IP \[bu] 2 Download URL .PP Example: .IP .EX Arle Nadja From Puyo Puyo cidoku By SEGA/Compile http://cidoku.net/files/Arle_Nadja.AVB .EE .SS Poses A pose\[cq]s filename is designed to include all of the relevant data necessary for its creation. The format is \[lq]id_expressionName_intensity_centerX_centerY.png\[rq], e.g.\ 1_neutral_0_125_92.png .PP Each segment is used as follows: .TP \f[B]ID\f[R] Must be a positive number. Images with the same ID define different expressions for the same pose. .TP \f[B]ExpressionName\f[R] Defines the expression being used. It must be one of the following: neutral, laugh, shrug, bored, angry, happy, scared, shout, sad, coy, me, you, wave. .TP \f[B]Intensity\f[R] Ranges from 1 to 10. For most poses, this defines where in the radial wheel the expression is found, with a larger number being found closer to the center of the wheel. Neutral, point, and waving poses can have any number because they\[cq]re handled specially by Comic Chat internally. At present, no collision checks are performed, so exercise caution. Additionally, ensure that \[lq]me\[rq], \[lq]you\[rq], and \[lq]wave\[rq] are used only once each. Multiple neutral poses are acceptable and in fact encouraged. .TP \f[B]CenterX and CenterY\f[R] The X and Y values, in pixels, defining the center of the character\[cq]s head. This is used to know where to place the chat bubbles. .SH PREPARING FILES Following is a brief description of how to create appropriate files. .SS Poses Poses may be of any dimension up to 400x400 (legally), and of 32\-bit color. Transparency is allowed, but it is one\-bit, determined somewhere in the icon file, which must be 40x40 in the upper\-left corner with an overall field size equivalent to any other pose (all poses should be the same size). It must be in one of the 15 colors of the VGA palette, and defaults to lime green. As such, it is necessary to make the entire background the given color. Comic Chat will zoom in or out and/or mirror a pose along its vertical axis at its discretion, so be mindful of this if attempting to add text or details towards the bottom of the image. Poses should be saved in such a way that they\[cq]re facing to the right. The final product is traditionally a 255\-color index png file, for filesize reasons, but that is not strictly necessary. The following accessory script may be of use when preparing the final input; it assumes it\[cq]s being run one level above two directories, orig and output, with orig containing the files to be transformed: .IP .EX #!/bin/sh mkdir \-p output cd orig || exit 1 for i in *.png; do orig=\[dq]$i\[dq] out=\[dq]../output/$i\[dq] if [ \[dq]$orig\[dq] = \[aq]icon.png\[aq] ]; then convert \[dq]$orig\[dq] \-dither FloydSteinberg \-colors 256 PNG8:\[dq]$out\[dq] convert \[dq]$out\[dq] \-background \[dq]rgb(255,255,255)\[dq] \-flatten PNG8:\[dq]$out\[dq] continue fi convert \[dq]$orig\[dq] \-dither FloydSteinberg \-colors 256 \-flop PNG8:\[dq]$out\[dq] convert \[dq]$out\[dq] \-background \[dq]rgb(0,255,0)\[dq] \-flatten PNG8:\[dq]$out\[dq] done .EE .PP Adjust the rgb background colors as appropriate to your use\-case; above, the icon is rendered with a white background and the poses with a lime green. .SS Icon Unlike the other files, the icon is rendered differently depending on where it appears; in the nicklist it is always rendered in 16\-color mode, but if it shows up in the \[lq]starring\[rq] line it will be rendered normally. The icon is never mirrored, so its facing is unaffected. Transparency appears to be ignored for the icon itself. Again, it must be surrounded by a single\-color field and be the same dimensions of the other poses. .SH EXIT STATUS .TP \f[B]0\f[R] Normal operation .TP \f[B]1\f[R] Erroneous operation; see the relevant output for details .SH AUTHORS Primary author: cidoku Documentation and minor touch\-ups: ShadowM00n .SH BUGS None known. Report bugs to cidoku \c .MT mail@cidoku.net .ME \c \&. .SH COPYRIGHT To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. .PP You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see \c .UR http://creativecommons.org/publicdomain/zero/1.0/ .UE \c \&. EOF } # CLI interactions case "$1" in -h|--help) usage exit ;; -m|--man) show_manual exit ;; -v|--version) printf "%s version %s\n" "$(basename "$0")" "$VERSION" exit ;; esac # Immediate sanity checks on strictly-required files [ ! -f "$1/info.txt" ] && printf '%s\n' "info.txt could not be found in directory \"$1\". Can't continue..." && exit [ ! -f "$1/icon.png" ] && printf '%s\n' "icon.png could not be found in directory \"$1\". Can't continue..." && exit # Read metadata from info.txt file while IFS= read -r line; do info+=("$line") done < "$1/info.txt" # The name will be used as the file name. According to Comic Chat lore, it's # better if the character's name is the same as its file name. We remove spaces # because spaces in filenames SUCK name=$(printf '%s' "${info[0]:0:255}" | tr ' ' '_') description="${info[1]:0:255}" author="${info[2]:0:60}" copyright="${info[3]:0:255}" url="${info[4]:0:255}" # These options set the character's sex and whether to "allow the user to # modify the URL". Neither option is revealed to the user at all, so who cares? sex=0 #"${info[5]:0:1}" mod_url=0 #"${info[6]:0:1}" # Identify character dimensions read -r width height <<< "$(identify -format '%w %h' "$1/icon.png")" # Build the file list dir=$(pwd); cd "$1" || (printf '%s\n' "directory $1 is inaccessible" && exit 1) list=(*.png); cd "$dir" || (printf '%s\n' "directory $dir is inaccessible\n" && exit 1) readarray -t files < <(printf '%s\n' "${list[@]}" | xargs -n 1 basename -s .png | sort -t _ -g) # Build the id list and count unique ids to get pose count for ((i=1; i<${#files[@]}; i++)); do ids[i]=$(printf '%s\n' "${files[i]}" | tr "_" " " | cut -d ' ' -f1) done poses=$(printf '%s\n' "${ids[@]}" | uniq | wc -l) # Prepare images list for later sprite sheet creation images[0]="$1/icon.png" # Metadata, part 1 meta=$(printf '%s' ' Microsoft Chat Character Source File ') meta=${meta}$(printf '%*s%*s%*s' '255' "$name" '255' "$description" '60' "$author") meta=${meta}$(printf ' %03d0%*s%03d%03d%03d0' '256' '15' ' ' "$width" "$height" "$poses") # Poses expressions=(neutral laugh shrug bored angry happy scared shout sad coy me you wave) for ((i=1; i<${#files[@]}; i++)); do IFS='_' read -r -a split <<< "$(printf '%s' "${files[$i]}")" id=${split[0]} images[id+1]="$1/${files[$i]}.png" emote="${split[1]}" intensity=$((split[2]+1)) x=$((split[3]-16)) y=$((split[4]-16)) exprstr="00000000000000000000000000" meta=${meta}$(printf '000%03d%03d10%*s' $x $y 25 "$emote") # Loop over pose metadata keep=1 while [ $keep = 1 ]; do found=0 for ((e=0; e<${#expressions[@]}; e++)); do if [ "$emote" = "${expressions[$e]}" ]; then a=$((e * 2)) b=$((e * 2 + 2)) # If neutral, pointing or waving, intensity is always "02" if (( e == 0 )) || (( e > 9 )); then exprstr="${exprstr:0:a}02${exprstr:b}" else exprstr="${exprstr:0:a}$(printf '%02d' $intensity)${exprstr:b}" fi found=1 break fi done (( found == 0 )) && printf '%s\n' "${files[$i]}.png has no valid expression name. Can't continue..." && exit # If the next id is the same, this means we have another expression in # the same pose next=$((i + 1)) if (( next < ${#files[@]} )) && (( ${ids[$next]} == id )); then i=$next IFS='_' read -r -a split <<< "$(printf '%s' "${files[$i]}")" emote="${split[1]}" intensity=$((split[2] + 1)) else keep=0 fi done meta=$meta$(printf '%s' "$exprstr") done # Metadata, part 2 meta=$meta$(printf '%d%*s%*s%*s%d ' $sex 255 "$copyright" 255 "$url" 31 " " $mod_url) # Create spritesheet and append metadata montage "${images[@]}" -tile 5 -geometry +0+0 -mode Concatenate out.png mv out.png "$name.avs" printf '%s' "$meta" >> "$name.avs" printf '%s\n' "Done! Character file saved to $name.avs." printf '%s\n' "You may now use the character editor to export it as a Comic Chat file."