#include <iostream>
#include <sstream>
#include <algorithm>
#include <functional>
#include <iterator>
#include <numeric>
#include <cstdint>
#include <cmath>
#include <bitset>
#include <string>
#include <list>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>

#define N_TIMES(i, n)     for ( uint64_t i = 0;     i <  n; ++i )
#define N_TIMES_REV(i, n) for (  int64_t i = n - 1; i >= 0; --i )

template<typename T>
struct Point2D {
  T x, y;

  Point2D(T x = 0, T y = 0) : x(x), y(y) {}

  bool operator==(const Point2D &p) const { return x == p.x && y == p.y; }
  bool operator!=(const Point2D &p) const { return x != p.x || y != p.y; }

  Point2D  operator+ (const Point2D &p) const { return Point2D(x + p.x, y + p.y); }
  Point2D  operator- (const Point2D &p) const { return Point2D(x - p.x, y - p.y); }
  Point2D& operator+=(const Point2D &p) { x += p.x; y += p.y; return *this; }
  Point2D& operator-=(const Point2D &p) { x -= p.x; y -= p.y; return *this; }

  Point2D  operator* (T k) const { return Point2D(x * k, y * k); }
  Point2D  operator/ (T k) const { return Point2D(x / k, y / k); }
  Point2D& operator*=(T k) { x *= k; y *= k; return *this; }
  Point2D& operator/=(T k) { x /= k; y /= k; return *this; }

  T operator*(const Point2D &p) const { return x * p.x + y * p.y; }

  T dist_pow2() const { return x * x + y * y; }

  bool operator< (const Point2D &p) const {
    return dist_pow2() == p.dist_pow2() 
         ? ( y == p.y
             ? x < p.x
             : y < p.y )
         : dist_pow2() <  p.dist_pow2();
  }
  bool operator> (const Point2D &p) const {
    return dist_pow2() == p.dist_pow2()
         ? ( y == p.y
             ? x > p.x
             : y > p.y )
         : dist_pow2() >  p.dist_pow2();
  }
  bool operator<=(const Point2D &p) const { return (*this == p) || (*this < p); }
  bool operator>=(const Point2D &p) const { return (*this == p) || (*this > p); }
};

typedef Point2D<int64_t> Point;

namespace std {
  template<> struct hash<Point> {
    size_t operator()(const Point &key) const {
      return (key.x & 0x5555555555555555L) | (key.y & 0xAAAAAAAAAAAAAAAAL);
    }
  };
};

using namespace std;

int main()
{
  uint64_t H, W;
  cin >> H >> W;
  
  Point S(0, 0), G(W - 1, H - 1);
  auto isInGrid = [&H, &W](const Point &p) { return (0 <= p.x && p.x < W) && (0 <= p.y && p.y < H); };

  vector<string> s(H);
  N_TIMES(h, H) {
    cin >> s[h];
  }
  auto isWhiteCell = [&s](const Point &p) { return s[p.y][p.x] == '.'; };

  vector<vector<uint64_t>> dist(H);
  N_TIMES(h, H) { dist[h] = vector<uint64_t>(W, -1); }
  dist[S.y][S.x] = 1;

  queue<Point> q;
  q.emplace(S);
  while (!q.empty()) {
    Point p = q.front(); q.pop();
    if (p == G) { break; }

    uint64_t new_dist = dist[p.y][p.x] + 1;
    int64_t dx[4] = {-1, +1, 0, 0}, dy[4] = {0, 0, -1, +1};
    const Point dirs[4] = {Point(-1, 0), Point(+1, 0), Point(0, -1), Point(0, +1)};
    N_TIMES(i, 4) {
      Point r = p + dirs[i];
      if (!isInGrid(r) || !isWhiteCell(r)) { continue; }

      if (new_dist < dist[r.y][r.x]) {
        q.emplace(r);
        dist[r.y][r.x] = new_dist;
      }
    }
  }

  if (dist[G.y][G.x] == static_cast<uint64_t>(-1)) {
    cout << -1 << endl;
    return 0;
  }
  
  uint64_t numberOfBlackCells = 0;
  N_TIMES(h, H) {
    N_TIMES(w, W) {
      if (s[h][w] == '#') { ++numberOfBlackCells; }
    }
  }
  
  uint64_t ans = (H * W) - (dist[G.y][G.x] + numberOfBlackCells);
  cout << ans << endl;

  return 0;
}

Measured memory_bytes: 256