////////////////////////////////////////////////////////////////////
// This source code is for Visual Studio
////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <sstream>
#include <iterator>
#include <stack>
#include <functional>
#include <iomanip>
#include <string>
#include <cstring>
#include <deque>
#include <math.h>
#include <assert.h>

#define	numberof(a)	(sizeof(a) / sizeof(a[0]))
#define	INF		INT32_MAX
#define Rep(i,n) for(int i = 0; i < (n); ++i )

using namespace std;

typedef vector< vector<int> > MAT;
typedef pair<int, int> PINT;
typedef long long ll;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int32_t s32;
typedef int64_t s64;

//constant
//--------------------------------------------
const double EPS = 1e-10;
const double PI  = acos(-1.0);

struct edge
{
	ll from;
	ll to;
	ll cost;

	edge(ll inp_from, ll inp_to, ll inp_cost) {
		from = inp_from;
		to = inp_to;
		cost = inp_cost;
	}

	// コストの大小で順序定義
	bool operator<(const edge& o) const {
		return cost < o.cost;
	}
};

bool edge_comp(const edge& e1, const edge& e2)
{
	return e1.cost < e2.cost;
}

////////////////////
// UnionFind
////////////////////
class UnionFind
{
public:
	vector<int> parent;
	vector<int> rank;

	UnionFind(int n)
	{
		Rep(i, n) {
			parent.push_back(i);
			rank.push_back(1);
		}
	}
	int root(int x)
	{
		if (parent[x] != x) parent[x] = root(parent[x]);
		return parent[x];
	}
	// is same union between x and y
	bool same(int x, int y)
	{
		return root(x) == root(y);
	}
	void unite(int x, int y)
	{
		int rx = root(x);
		int ry = root(y);
		if (rx == ry) return;
		if (rank[rx] > rank[ry]) {
			parent[ry] = rx;
			rank[rx] += rank[ry];
		}
		if (rank[rx] <= rank[ry]) {
			parent[rx] = ry;
			rank[ry] += rank[rx];
		}
	}
};

////////////////////
// kruskal(最小全域木)
////////////////////
vector< edge > shortest_edges;
ll kruskal(vector<edge> & edges, ll V)	// edgesは辺の集合、Vは頂点数
{
	UnionFind uf(V);// Union-Findの初期化
	ll res = 0;

	sort(edges.begin(), edges.end(), edge_comp); // edge.costの小さい順

	for (int i = 0; i < edges.size(); i++)
	{
		edge e = edges[i];
		if (!uf.same(e.from, e.to))
		{
			uf.unite(e.from, e.to);
			res += e.cost;

			// save
			shortest_edges.push_back(e);
		}
	}

	return res;	// 最小の重み
}

////////////////////
// 回文判定 O(N)
////////////////////
bool isPalindrome(string input_str)
{
	bool ret = true;
	ll i = 0;
	ll j = input_str.length() - 1;

	while (ret && i <= j) {
		ret = ret & (input_str[i] == input_str[j]);
		i++;
		j--;
	}
	return ret;
}

////////////////////
// 2分探索 O(logN)
// ※ソートされていることが前提
////////////////////
ll binary_search(ll target, vector<ll> & search_table)
{
	const ll loop_cnt = 1000;	// 要調整
	ll N = search_table.size();
	ll lb = 0;
	ll ub = N;

	for (int i = 0; i < loop_cnt; ++i) {
		ll mid = (lb + ub) / 2;

		if (target == search_table[mid]) {
			return mid;
		}
		else if (target >= search_table[mid]) {
			lb = mid;
		}
		else {
			ub = mid;
		}
	}

	return ub;
}

bool throw_comp(const pair<ll, ll> & p1, const pair<ll, ll> & p2)
{
	// sort(item.begin(), item.end(), throw_comp)のように使う
	return p1.second > p2.second;
}

bool tmp_comp(const pair<ll, ll> & p1, const pair<ll, ll> & p2)
{
	// sort(item.begin(), item.end(), tmp_comp)のように使う
	return ((double)p1.first / p1.second) > ((double)p2.first / p2.second);
}

////////////////////
// しゃくとり法
// 要素数Nの配列A[N]
////////////////////
ll shakutori( ll N, vector<ll> & A )
{
	ll ans = 0;
	ll head = 0;
	ll tail = 0;
	set<ll> memo;

	for (tail = 0; tail < N; tail++) {
		// [head, tail]に同じ数字が2つ以上含まれているときheadを進める
		while (memo.count(A[tail]) != 0) {
			memo.erase(A[head]);
			head++;
		}

		ans = max(ans, tail - head + 1);
		memo.insert(A[tail]);
	}

	return ans;
}

// 数学：		最大公約数、最小公倍数、素因数分解
// 探索：		貪欲法(全探索)、2分探索、ソート
//				BFS(幅優先探索)、DFS(深さ優先探索)
// 探索(発展)：	貪欲法、しゃくとり法、区間累積和
// データ構造：	スタック、キュー、deque
//				ハッシュ、map、リスト
// DP:			桁DP、ビットDP、メモ化再帰
// グラフ:		最短経路(ダイクストラ、ベルマンフォード、ワーシャルフロイト)
//				最小全域木(クラスカル法、プリム法、Union-Find)
//				三角不等式
// その他：		最大フロー、マッチング問題
//				セグメント木、RMQ、BIT、平方分割
// 
//ll dp[500000 * 2000 + 10];	// dp[i日目]までの勝ち数


// @param[in] G[i][j]:ノードiにつながっているj番目のノード(firstがノード番号, secondがコスト。つながっているノードだけ含まれる)
// @param[in] startNode:起点ノード番号
// @retval : 起点からノードiへの最短経路の重み、INFで初期化
vector<ll> dijkstra(vector< vector< pair<ll, ll> > > & G, ll startNode)
{
	priority_queue< pair<ll, ll> > searchPQ;	// PQの要素はこれから探索するノード(firstがコスト, secondがノード番号)
	const ll node_number = G.size();
	vector<bool> isChecked(node_number, false);	// trueのときは探索済
	vector<ll> output_dist(node_number);

	Rep(i, node_number) {
		output_dist[i] = INF;
		isChecked[i] = false;
	}

	// 起点からスタート
	output_dist[startNode] = 0;
	searchPQ.push(make_pair(0, startNode));

	while (!searchPQ.empty())
	{
		pair<ll, ll> search_node = searchPQ.top(); searchPQ.pop();
		ll u = search_node.second;// 調べるノード
		isChecked[u] = true;

		if (output_dist[u] < search_node.first * (-1)) continue;// search_node.firstは負の値にしている

		for (ll j = 0; j < G[u].size(); j++) {
			// uにつながっているノードを探索
			ll v = G[u][j].first;
			if (isChecked[v] == true) continue;
			if (output_dist[v] > output_dist[u] + G[u][j].second) {
				output_dist[v] = output_dist[u] + G[u][j].second;
				searchPQ.push(make_pair(output_dist[v] * (-1), v));//priority_queueはデフォルトで大きい値をpopしてしまうのでマイナスをかける
			}
		}
	}

	return output_dist;
}

typedef pair<ll, ll> iPair;
struct Graph
{
	/*
	example).
	int V = 9;
	Graph g(V);

	//  making above shown graph
	g.addEdge(0, 1, 4);
	g.addEdge(0, 7, 8);
	g.addEdge(1, 2, 8);
	g.addEdge(1, 7, 11);
	g.addEdge(2, 3, 7);
	g.addEdge(2, 8, 2);
	g.addEdge(2, 5, 4);
	g.addEdge(3, 4, 9);
	g.addEdge(3, 5, 14);
	g.addEdge(4, 5, 10);
	g.addEdge(5, 6, 2);
	g.addEdge(6, 7, 1);
	g.addEdge(6, 8, 6);
	g.addEdge(7, 8, 7);

	g.shortestPath(0);// start_node = 0
	*/
	ll V;    // No. of vertices

	// In a weighted graph, we need to store vertex
	// and weight pair for every edge
	vector< vector< pair<ll, ll> > > adj;// adjacent list. each element is pair(dist, vertex)

	// constructor
	Graph(ll V)
	{
		this->V = V;
		adj.resize(V);
	}

	// function to add an edge to graph
	void addEdge(ll u, ll v, ll weight)
	{
		adj[u].push_back(make_pair(v, weight));
		adj[v].push_back(make_pair(u, weight));
	}

	// get shortest path from src node
	vector<ll> shortestPath(ll src)
	{
		priority_queue< iPair, vector <iPair>, greater<iPair> > pq;

		// initialize all distances as infinite (INF)
		vector<ll> dist(V, INF);

		// Insert source itself in priority queue and initialize
		pq.push(make_pair(0, src));// (dist, vertex)
		dist[src] = 0;

		while (!pq.empty())
		{
			// The first vertex in pair is the minimum distance vertex, extract it from priority queue.
			// (it has to be done this way to keep the vertices sorted distance)
			// (distance must be first item in pair. vertex label is stored in second of pair)
			ll u = pq.top().second;// (dist, vertex)
			pq.pop();

			for (ll i = 0; i < adj[u].size(); ++i)
			{
				// Get vertex label and weight of current adjacent of u.
				ll v = adj[u][i].first;
				ll weight = adj[u][i].second;

				//  If there is shorted path to v through u.
				if (dist[v] > dist[u] + weight)
				{
					dist[v] = dist[u] + weight;
					pq.push(make_pair(dist[v], v));
				}
			}
		}

		//printf("Vertex Distance from Source\n");
		//for (int i = 0; i < V; ++i) printf("%d \t\t %d\n", i, dist[i]);

		return dist;
	}
};

int main()
{
	ll H = 0;
	ll W = 0;
	ll ans = 0;
	ll black = 0;

	cin >> H >> W;
	vector< vector<char> > floor(H, vector<char>(W, '.'));
	Rep(y, H) {
		Rep(x, W) {
			cin >> floor[y][x];
			if ('#' == floor[y][x]) black++;
		}
	}

	vector< vector< pair<ll, ll> > > G(H * W);// (x, y) -> node (y * W + x)
	Graph graph(H * W);
	int dx[] = { 0, 0,  -1, 1 };
	int dy[] = { 1, -1,  0, 0 };

	Rep(y, H) {
		Rep(x, W) {
			// check (x, y)
			ll node = y * W + x;

			// search around node
			Rep(k, 4) {
				ll new_x = x + dx[k];
				ll new_y = y + dy[k];
				ll new_node = new_y * W + new_x;
				if (new_x < 0 || new_x >= W) continue;
				if (new_y < 0 || new_y >= H) continue;

				if ('#' == floor[new_y][new_x]) {
					//G[node].push_back(make_pair(new_node, INF));
				}
				else if ('.' == floor[new_y][new_x]) {
					//graph.addEdge(node, new_node, 1);
					G[node].push_back(make_pair(new_node, 1));
				}
			}
		}
	}

	// solve
	// (0, 0) - (W - 1, H - 1)
//	vector<ll> result = graph.shortestPath(0);// start_node = 0
	vector<ll> result = dijkstra(G, 0);
	if (result[(H - 1) * W + W - 1] == INF) {
		ans = -1;
	}
	else {
		ll black_enable = H * W - result[(H - 1) * W + W - 1] - 1;
		ans = black_enable - black;
	}

	cout << ans << endl;

	return 0;
}


Measured memory_bytes: 640