Here's C code to cut up a (6.1) ASCII FBX animation file into separate keyframe files

I wrote this to cut up a couple of animations that I use piecemeal in two Blend Space 1Ds because I got tired of having to re-clip frames every time I changed the underlying animation. Now I can run a script to cut out the frames I want after changing the animation and just re-import the individual keyframes.

This only works on ASCII FBX files, and I’ve only tried it on a file exported from Blender 2.79 (2.8 doesn’t even have ASCII FBX export now). I know it doesn’t produce exactly the same coordinates but the difference doesn’t seem to be significant.
There are a lot of sharp edges, use at your own risk. It only reads the source file but overwrites any previously-generated files.
See the usage text for more details on how to use.




// A quick and dirty C program to take in an ASCII 6.1 FBX file and create
// ASCII FBX files for each keyframe.  I'm tossing this into the public domain.
// brian at westley period org

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

int main(int argc, char *argv])
{
  char line[256], line2[256], line3[256], line4[256], line5[256];
  char *p, *q, fname[256];
  FILE *fbx, *fbxout;
  int i, j, k, m, n;
  int frame, kf, ki;
  long long ll, keys[256];
  char allkeys;
  int didakey;
  float percent;
  char *suffix = "_", pchar = 'p', dotchar = '.', minuschar = '-';
  char *format = "%03d";

  if (argc < 2 ||
      !((p=strstr(argv[1], ".fbx")) != NULL && strlen(p) == 4))
  {
    printf("Usage: %s file.fbx [arg1] [arg2] ...

", argv[0]);
    printf("This program takes an ASCII FBX animation file and creates FBX
");
    printf("files that contain a single frame of the original animation.

");
    printf("If no arguments other than the filename are given, all frames
");
    printf("are output in files named file_000.fbx file_001.fbx etc.

");
    printf("Arguments can be frame numbers (0 based) or percents.
");
    printf("Negative frame numbers are counted from the end, e.g. -0 is
");
    printf("the last frame, -1 is second to last, etc.
");
    printf("Percents are arguments with a '%%' or a '.' and will contain
");
    printf("the closest frame.

");
    printf("-f<format> changes the frame numbering to <format> (default %%03d).
");
    printf("-s<suffix> changes the suffix to <suffix> (default _).
");
    printf("file names are generated directly from arguments with the
");
    printf("following single-character substitutions:
");
    printf("--x will substitute x in place of - for frame arguments (default -)
");
    printf("-.x will substitute x in place of . for percent arguments (default .)
");
    printf("-%%x will substitute x in place of %% for percent arguments (default p)
");
    printf("Special cases for -%%:
");
    printf("-%%# will output the frame format instead of copying the arguments.
");
    printf("-%%s will ONLY use the current suffix for the new filename.

");
    printf("Examples:
");
    printf("keyframefbx file.fbx 0 7 14
");
    printf("This will create file_0.fbx, file_7.fbx, and file_14.fbx

");
    printf("keyframefbx file.fbx 0%% -f_FRAME%%04d 50%% -%%# 100%%
");
    printf("This will create file_0p.fbx, file_50p.fbx, and file_FRAMENNNN.fbx
");
    printf("where NNNN is the last (zero-filled) frame number.

");
    printf("keyframefbx file.fbx -%%s -s_start 0 -s_middle 50%% -s_end -0
");
    printf("This will create file_start.fbx, file_middle.fbx, and file_end.fbx
");
    exit(-1);
  }

  for (i = 0; i < sizeof(keys)/sizeof(keys[0]); i++)
    keys* = -1;
  k = 0;

  fbx = fopen(argv[1], "r");

  while (fgets(line, sizeof(line), fbx) != NULL)
  {
    if ((p=strstr(line, "KeyCount: ")) != NULL)
    {
      i = atoi(p+10);
      fgets(line, sizeof(line), fbx);
      while (i-- > 0)
      {
        fgets(line, sizeof(line), fbx);
        ll = atoll(line);
        j = 0;
        while (j < k && keys[j] < ll)
          j++;
        if (keys[j] != ll)
        {
          m = j;
          n = k;
          while (j < k)
          {
            keys[n] = keys[n-1];
            j++;
            n--;
          }
          keys[m] = ll;
          k++;
        }
      }
    }
  }

  fclose(fbx);
  // k = total key frames
  //for (i = 0; i < k; i++)
    //printf("%lld
", keys*);

  // If list of frames to use is given, use those, otherwise do all
  allkeys = (argc == 2);
  if (allkeys)
  {
    kf = k;
  }
  else
  {
    kf = argc - 2;
  }

  didakey = 0;
  for (ki = 0; ki < kf; ki++)
  {
    if (allkeys)
    {
      frame = ki;
      sprintf(line, format, frame);
    }
    else
    {
      strcpy(line, argv[ki+2]);
      if (line[0] == '-')
      {
        switch (line[1])
        {
        case 's':
          suffix = argv[ki+2]+2;
          break;
        case 'f':
          format = argv[ki+2]+2;
          break;
        case '%':
          pchar = line[2];
          break;
        case '.':
          dotchar = line[2];
          break;
        case '-':
          minuschar = line[2];
          break;
        }
        // if we've only done switches and have no explicit keyframes,
        // reset the numbers and allkeys
        if (ki+1 == kf && didakey == 0)
        {
          allkeys = 1;
          ki = 0;
          kf = k;
          continue;
        }
      }
      didakey = 1;
      q = NULL;
      if ((p=index(line, '%')) != NULL ||
          (q=index(line, '.')) != NULL)
      {
        percent = atof(line);
        if (p != NULL)
          p[0] = pchar;
        if (q != NULL)
          q[0] = dotchar;
        frame = percent * (float)k / 100.0 + 0.5;
      }
      else
      {
        frame = strtol(line, NULL, 10);
        if (line[0] == '-')
        {
          frame = k - 1 + frame;
          p[0] = minuschar;
        }
      }
      if (frame < 0)
        frame = 0;
      if (frame >= k)
        frame = k-1;
      if (pchar == 's')
        line[0] = '\0';
      else if (pchar == '#')
        sprintf(line, format, frame);
    }

    fbx = fopen(argv[1], "r");

    snprintf(fname, sizeof(fname), "%s", argv[1]);
    i = strlen(fname)-4;
    snprintf(fname+i, sizeof(fname)-i, "%s%s.fbx", suffix, line);
    fbxout = fopen(fname, "w");

    while (fgets(line, sizeof(line), fbx) != NULL)
    {
      if (strstr(line, "LocalTime: ") != NULL ||
          strstr(line, "ReferenceTime: ") != NULL)
      {
        if ((p=index(line, ',')) != NULL)
        {
          p[1] = '0';
          p[2] = '
';
          p[3] = '\0';
        }
        fputs(line, fbxout);
      }
      else if ((p=strstr(line, "TimeLineStopTime: ")) != NULL)
      {
        p[18] = '0';
        p[19] = '
';
        p[20] = '\0';
        fputs(line, fbxout);
      }
      else if ((p=strstr(line, "Default: ")) != NULL)
      {
        p[9] = '\0';
        fprintf(fbxout, "%s", line);

        fgets(line2, sizeof(line2), fbx);
        fgets(line3, sizeof(line3), fbx);

        if ((p=strstr(line3, "KeyCount: ")) != NULL)
        {
          i = atoi(p+10);
          p[10] = '1';
          p[11] = '
';
          p[12] = '\0';
          fgets(line4, sizeof(line4), fbx);
          while (i-- > 0)
          {
            fgets(line5, sizeof(line5), fbx);
            ll = atoll(line5);
            if (keys[frame] <= ll || i == 0)
            {
              if (line5[strlen(line5)-2] == ',')
              {
                line5[strlen(line5)-2] = '
';
                line5[strlen(line5)-1] = '\0';
              }
              if ((q=index(line5, ',')) != NULL)
                q[0] = '\0';
              else
                q = "??
";
              if ((p=rindex(line5, '	')) != NULL)
                p[1] = '\0';
              break;
            }
          }
          if ((p=index(q+1, ',')) != NULL)
          {
            p[0] = '\0';
            fprintf(fbxout, "%s
", q+1);
            p[0] = ',';
          }
          else
          {
            fprintf(fbxout, "%s", q+1);
          }
          fputs(line2, fbxout);
          fputs(line3, fbxout);
          fputs(line4, fbxout);
          fprintf(fbxout, "%s%lld,%s", line5, keys[frame], q+1);
          while (i-- > 0)
            fgets(line, sizeof(line), fbx);
        }
      }
      else
      {
        fputs(line, fbxout);
      }
    }

    fclose(fbx);
    fclose(fbxout);
  }

  return (0);
}