#include <iostream>
#include <string>
#include <tuple>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <assert.h>

using namespace std;

class Point {
public:
  Point()
    : x(-1), y(-1) {}
  Point(int x_, int y_)
    : x(x_), y(y_) {}
  Point(const Point& other)
    : x(other.x), y(other.y) {}

  static Point from_rc(int r, int c) {
    return Point(c, r);
  }
  
  void print() {
    cout << "(" << x << ", " << y << ")" << endl;
  }
  
  bool operator==(const Point& other) const {
    return x == other.x && y == other.y;
  }
  
  bool operator<(const Point& other) const {
    return (y < other.y) || (y == other.y && x < other.x);
  }

  Point left() const { return Point(x - 1, y); }
  Point right() const { return Point(x + 1, y); }
  Point up() const { return Point(x, y - 1); }
  Point down() const { return Point(x, y + 1); }

  int x;
  int y;
};

class Maze {
public:
  Maze(int r, int c) : row(r), col(c) {
    is_availables = new bool [r*c];
    for (int y = 0; y < r; y++) {
      for (int x = 0; x < c; x++) {
	is_availables[y*c+x] = false;
      }
    }
  }

  ~Maze() {
    delete is_availables;
  }

  int num_of_repaints(Point& s, Point& g);
  vector<Point> least(Point& s, Point& g);
  void add_targets(vector<Point>& path, Point& p, queue<tuple<vector<Point>, Point> >& targets);
  
  void make_path(int r, int c) {
    is_availables[r*col+c] = true;
  }

  bool is_path(Point& p) {
    return (0 <= p.x && p.x < col) && (0 <= p.y && p.y < row) && is_availables[p.y*col+p.x];
  }

  int num_of_paths() {
    int n = 0;
    for (int y = 0; y < row; y++) {
      for (int x = 0; x < col; x++) {
	if (is_availables[y*col+x]) {
	  n++;
	}
      }
    }
    return n;
  }
  
  int row;
  int col;
  bool* is_availables;
};

void Maze::add_targets(vector<Point>& path, Point& p, queue<tuple<vector<Point>, Point> >& targets)
{
  Point t;
  t = p.left(); if (is_path(t)) {
    vector<Point> new_path = path;
    new_path.push_back(t);
    targets.push(make_tuple(new_path, t));
  }
  t = p.right(); if (is_path(t)) {
    vector<Point> new_path = path;
    new_path.push_back(t);
    targets.push(make_tuple(new_path, t));
  }
  t = p.up(); if (is_path(t)) {
    vector<Point> new_path = path;
    new_path.push_back(t);
    targets.push(make_tuple(new_path, t));
  }
  t = p.down(); if (is_path(t)) {
    vector<Point> new_path = path;
    new_path.push_back(t);
    targets.push(make_tuple(new_path, t));
  }
}
  
vector<Point> Maze::least(Point& s, Point& g)
{
  set<Point> reached;
  queue<tuple< vector<Point>, Point> > targets;
  targets.push(make_tuple(vector<Point>(), s));

  while (!targets.empty()) {
    auto pair = targets.front();
    vector<Point> path = get<0>(pair);
    Point p = get<1>(pair);
    targets.pop();
    if (reached.find(p) != reached.end()) {
      continue;
    }
    //    p.print();

    if (g == p) {
      path.push_back(p);
      return path;
    }
    reached.insert(p);
    add_targets(path, p, targets);
  }

  return vector<Point>();
}

int Maze::num_of_repaints(Point& s, Point& g)
{
  vector<Point> path = least(s, g);
  if (path.empty()) {
    return -1;
  } else {
    return num_of_paths() - path.size();
  }
}

int main()
{
  int h, w;
  cin >> h >> w;

  Point start(0, 0);
  Point goal(w - 1, h - 1);
  
  Maze m(h, w);
  for (int r = 0; r < h; r++) {
    for (int c = 0; c < w; c++) {
      char c0;
      cin >> c0;
      if (c0 == '.') {
	m.make_path(r, c);
      }
    }
  }
  
  cout << m.num_of_repaints(start, goal) << endl;
  
  return 0;
}


Measured memory_bytes: 512